More Storage

This commit is contained in:
Vankka 2022-02-20 00:57:42 +02:00
parent a40ac91096
commit ee1db84ae0
No known key found for this signature in database
GPG Key ID: 6E50CB7A29B96AD0
23 changed files with 362 additions and 29 deletions

View File

@ -28,6 +28,9 @@ shadowJar {
'io.leangen.geantyref', 'io.leangen.geantyref',
'org.yaml.snakeyaml', 'org.yaml.snakeyaml',
// HikariCP
'com.zaxxer.hikari',
// Adventure, EnhancedLegacyText, MCDiscordReserializer // Adventure, EnhancedLegacyText, MCDiscordReserializer
'net.kyori', 'net.kyori',
'dev.vankka.enhancedlegacytext', 'dev.vankka.enhancedlegacytext',

View File

@ -38,6 +38,7 @@ import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.messageforwarding.game.MinecraftToDiscordChatModule; import com.discordsrv.common.messageforwarding.game.MinecraftToDiscordChatModule;
import com.discordsrv.common.plugin.PluginManager; import com.discordsrv.common.plugin.PluginManager;
import com.discordsrv.common.server.ServerDiscordSRV; import com.discordsrv.common.server.ServerDiscordSRV;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import org.bukkit.Server; import org.bukkit.Server;
import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.ServicePriority;
@ -150,6 +151,11 @@ public class BukkitDiscordSRV extends ServerDiscordSRV<BukkitConfig, BukkitConne
return OnlineMode.of(server().getOnlineMode()); return OnlineMode.of(server().getOnlineMode());
} }
@Override
public ClasspathAppender classpathAppender() {
return bootstrap.getClasspathAppender();
}
@Override @Override
public ConnectionConfigManager<BukkitConnectionConfig> connectionConfigManager() { public ConnectionConfigManager<BukkitConnectionConfig> connectionConfigManager() {
return connectionConfigManager; return connectionConfigManager;

View File

@ -30,6 +30,7 @@ import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.plugin.PluginManager; import com.discordsrv.common.plugin.PluginManager;
import com.discordsrv.common.scheduler.StandardScheduler; import com.discordsrv.common.scheduler.StandardScheduler;
import com.discordsrv.proxy.ProxyDiscordSRV; import com.discordsrv.proxy.ProxyDiscordSRV;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import net.kyori.adventure.platform.bungeecord.BungeeAudiences; import net.kyori.adventure.platform.bungeecord.BungeeAudiences;
import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.Plugin;
@ -114,6 +115,11 @@ public class BungeeDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionConf
return OnlineMode.of(proxy().getConfig().isOnlineMode()); return OnlineMode.of(proxy().getConfig().isOnlineMode());
} }
@Override
public ClasspathAppender classpathAppender() {
return bootstrap.getClasspathAppender();
}
@Override @Override
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() { public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
return null; return null;

View File

@ -4,7 +4,7 @@ configurations {
hikari hikari
h2Driver h2Driver
mysqlDriver mysqlDriver
compileOnly.extendsFrom hikari compileOnly.extendsFrom hikari, h2Driver, mysqlDriver
testRuntimeOnly.extendsFrom runtimeDownloadOnly testRuntimeOnly.extendsFrom runtimeDownloadOnly
} }
@ -13,6 +13,7 @@ task generateResourceForHikari(type: GenerateDependencyDownloadResourceTask) {
configuration = conf configuration = conf
file = 'dependencies/' + conf.name + '.txt' file = 'dependencies/' + conf.name + '.txt'
relocate 'com.zaxxer.hikari', 'com.discordsrv.dependencies.com.zaxxer.hikari' relocate 'com.zaxxer.hikari', 'com.discordsrv.dependencies.com.zaxxer.hikari'
relocate 'org.slf4j', 'com.discordsrv.dependencies.org.slf4j'
} }
task generateResourceForH2Driver(type: GenerateDependencyDownloadResourceTask) { task generateResourceForH2Driver(type: GenerateDependencyDownloadResourceTask) {
var conf = configurations.h2Driver var conf = configurations.h2Driver

View File

@ -33,12 +33,14 @@ import com.discordsrv.common.config.connection.ConnectionConfig;
import com.discordsrv.common.config.main.MainConfig; import com.discordsrv.common.config.main.MainConfig;
import com.discordsrv.common.config.manager.ConnectionConfigManager; import com.discordsrv.common.config.manager.ConnectionConfigManager;
import com.discordsrv.common.config.manager.MainConfigManager; import com.discordsrv.common.config.manager.MainConfigManager;
import com.discordsrv.common.dependency.DependencyLoader;
import com.discordsrv.common.discord.api.DiscordAPIEventModule; import com.discordsrv.common.discord.api.DiscordAPIEventModule;
import com.discordsrv.common.discord.api.DiscordAPIImpl; import com.discordsrv.common.discord.api.DiscordAPIImpl;
import com.discordsrv.common.discord.connection.DiscordConnectionManager; import com.discordsrv.common.discord.connection.DiscordConnectionManager;
import com.discordsrv.common.discord.connection.jda.JDAConnectionManager; import com.discordsrv.common.discord.connection.jda.JDAConnectionManager;
import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl; import com.discordsrv.common.discord.details.DiscordConnectionDetailsImpl;
import com.discordsrv.common.event.bus.EventBusImpl; import com.discordsrv.common.event.bus.EventBusImpl;
import com.discordsrv.common.exception.StorageException;
import com.discordsrv.common.function.CheckedFunction; import com.discordsrv.common.function.CheckedFunction;
import com.discordsrv.common.function.CheckedRunnable; import com.discordsrv.common.function.CheckedRunnable;
import com.discordsrv.common.groupsync.GroupSyncModule; import com.discordsrv.common.groupsync.GroupSyncModule;
@ -60,6 +62,7 @@ import com.discordsrv.common.placeholder.PlaceholderServiceImpl;
import com.discordsrv.common.placeholder.context.GlobalTextHandlingContext; import com.discordsrv.common.placeholder.context.GlobalTextHandlingContext;
import com.discordsrv.common.profile.ProfileManager; import com.discordsrv.common.profile.ProfileManager;
import com.discordsrv.common.storage.Storage; import com.discordsrv.common.storage.Storage;
import com.discordsrv.common.storage.StorageType;
import net.dv8tion.jda.api.JDA; import net.dv8tion.jda.api.JDA;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -68,6 +71,7 @@ import java.util.Locale;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
@ -333,6 +337,29 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
throw t; throw t;
} }
// Storage
try {
try {
StorageType storageType = getStorageType();
if (storageType.hikari()) {
DependencyLoader.hikari(this).process(classpathAppender()).get();
}
storage = storageType.storageFunction().apply(this);
storage.initialize();
} catch (ExecutionException e) {
throw new StorageException(e.getCause());
} catch (StorageException e) {
throw e;
} catch (Throwable t) {
throw new StorageException(t);
}
} catch (StorageException e) {
e.log(this);
logger().error("Startup cancelled because of storage connection failure");
setStatus(Status.FAILED_TO_START);
return;
}
discordConnectionManager = new JDAConnectionManager(this); discordConnectionManager = new JDAConnectionManager(this);
discordConnectionManager.connect().join(); discordConnectionManager.connect().join();
@ -357,6 +384,15 @@ public abstract class AbstractDiscordSRV<C extends MainConfig, CC extends Connec
registerModule(LeaveMessageModule::new); registerModule(LeaveMessageModule::new);
} }
private StorageType getStorageType() {
String backend = connectionConfig().storage.backend;
switch (backend.toLowerCase(Locale.ROOT)) {
case "h2": return StorageType.H2;
case "mysql": return StorageType.MYSQL;
}
throw new StorageException("Unknown storage backend \"" + backend + "\"");
}
@OverridingMethodsMustInvokeSuper @OverridingMethodsMustInvokeSuper
protected void disable() { protected void disable() {
Status status = this.status.get(); Status status = this.status.get();

View File

@ -40,6 +40,7 @@ import com.discordsrv.common.plugin.PluginManager;
import com.discordsrv.common.scheduler.Scheduler; import com.discordsrv.common.scheduler.Scheduler;
import com.discordsrv.common.storage.Storage; import com.discordsrv.common.storage.Storage;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -59,6 +60,7 @@ public interface DiscordSRV extends DiscordSRVApi {
String version(); String version();
PluginManager pluginManager(); PluginManager pluginManager();
OnlineMode onlineMode(); OnlineMode onlineMode();
ClasspathAppender classpathAppender();
// DiscordSRVApi // DiscordSRVApi
@Override @Override

View File

@ -1,3 +1,21 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.config.connection; package com.discordsrv.common.config.connection;
import org.spongepowered.configurate.objectmapping.ConfigSerializable; import org.spongepowered.configurate.objectmapping.ConfigSerializable;

View File

@ -33,6 +33,10 @@ import java.util.concurrent.Executor;
public class DependencyLoader { public class DependencyLoader {
public static DependencyLoader hikari(DiscordSRV discordSRV) {
return new DependencyLoader(discordSRV, new String[] {"dependencies/hikari.txt"});
}
public static DependencyLoader h2(DiscordSRV discordSRV) { public static DependencyLoader h2(DiscordSRV discordSRV) {
return new DependencyLoader(discordSRV, new String[] {"dependencies/h2Driver.txt"}); return new DependencyLoader(discordSRV, new String[] {"dependencies/h2Driver.txt"});
} }

View File

@ -1,12 +1,45 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.exception; package com.discordsrv.common.exception;
import com.discordsrv.common.DiscordSRV;
public class StorageException extends RuntimeException { public class StorageException extends RuntimeException {
public StorageException(Throwable cause) { public StorageException(Throwable cause) {
super(cause); super(null, cause);
} }
public StorageException(String message) { public StorageException(String message) {
super(message); super(message);
} }
public void log(DiscordSRV discordSRV) {
String baseMessage = "Failed to initialize storage";
Throwable cause = getCause();
String message = getMessage();
if (cause != null && message != null) {
discordSRV.logger().error(baseMessage, this);
} else if (message != null) {
discordSRV.logger().error(baseMessage + ": " + message);
} else if (cause != null) {
discordSRV.logger().error(baseMessage, cause);
}
}
} }

View File

@ -1,3 +1,21 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.function; package com.discordsrv.common.function;
@FunctionalInterface @FunctionalInterface

View File

@ -34,6 +34,7 @@ public class DependencyLoggingHandler implements LogAppender {
static { static {
// Class names here will get relocated, which is fine // Class names here will get relocated, which is fine
LOGGER_MAPPINGS.put("net.dv8tion.jda", "JDA"); LOGGER_MAPPINGS.put("net.dv8tion.jda", "JDA");
LOGGER_MAPPINGS.put("com.zaxxer.hikari", "Hikari");
BLACKLISTED_MESSAGES.put("net.dv8tion.jda", Arrays.asList( BLACKLISTED_MESSAGES.put("net.dv8tion.jda", Arrays.asList(
// We have our own more informative log messages for this // We have our own more informative log messages for this
@ -42,6 +43,10 @@ public class DependencyLoggingHandler implements LogAppender {
"There was an I/O error while executing a REST request: ", "There was an I/O error while executing a REST request: ",
"There was an unexpected error while executing a REST request" "There was an unexpected error while executing a REST request"
)); ));
BLACKLISTED_MESSAGES.put("com.zaxxer.hikari", Collections.singletonList(
// This is fine, we don't need a warning about it
"was not found, trying direct instantiation." // "Registered driver with driverClassName={} was not found, trying direct instantiation."
));
} }
private final DiscordSRV discordSRV; private final DiscordSRV discordSRV;

View File

@ -0,0 +1,47 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.storage;
import com.discordsrv.common.DiscordSRV;
import com.discordsrv.common.storage.impl.sql.file.H2Storage;
import com.discordsrv.common.storage.impl.sql.hikari.MySQLStorage;
import java.util.function.Function;
public enum StorageType {
H2(H2Storage::new, false),
MYSQL(MySQLStorage::new, true);
private final Function<DiscordSRV, Storage> storageFunction;
private final boolean hikari;
StorageType(Function<DiscordSRV, Storage> storageFunction, boolean hikari) {
this.storageFunction = storageFunction;
this.hikari = hikari;
}
public Function<DiscordSRV, Storage> storageFunction() {
return storageFunction;
}
public boolean hikari() {
return hikari;
}
}

View File

@ -1 +1,19 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.storage.impl; package com.discordsrv.common.storage.impl;

View File

@ -1,3 +1,21 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.storage.impl.sql; package com.discordsrv.common.storage.impl.sql;
import com.discordsrv.common.exception.StorageException; import com.discordsrv.common.exception.StorageException;
@ -7,16 +25,14 @@ import com.discordsrv.common.storage.Storage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import java.sql.Connection; import java.sql.*;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.UUID; import java.util.UUID;
public abstract class SQLStorage implements Storage { public abstract class SQLStorage implements Storage {
public abstract Connection getConnection(); public abstract Connection getConnection();
public abstract boolean isAutoCloseConnections(); public abstract boolean isAutoCloseConnections();
public abstract void createTables(Connection connection) throws SQLException;
private void useConnection(CheckedConsumer<Connection> connectionConsumer) throws StorageException { private void useConnection(CheckedConsumer<Connection> connectionConsumer) throws StorageException {
useConnection(connection -> { useConnection(connection -> {
@ -47,11 +63,7 @@ public abstract class SQLStorage implements Storage {
@Override @Override
public void initialize() { public void initialize() {
useConnection(connection -> { useConnection(this::createTables);
try (Statement statement = connection.createStatement()) {
statement.execute("create table if not exists LINKED_ACCOUNTS (ID int not null auto_increment, PLAYER_UUID uuid, USER_ID bigint)");
}
});
} }
@Override @Override

View File

@ -1,3 +1,21 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.storage.impl.sql.file; package com.discordsrv.common.storage.impl.sql.file;
import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.DiscordSRV;
@ -11,6 +29,7 @@ import java.io.IOException;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties; import java.util.Properties;
public class H2Storage extends SQLStorage { public class H2Storage extends SQLStorage {
@ -40,7 +59,7 @@ public class H2Storage extends SQLStorage {
Properties.class, // info Properties.class, // info
String.class, // username String.class, // username
Object.class, // password Object.class, // password
Boolean.class // forbidCreation boolean.class // forbidCreation
); );
connection = (Connection) constructor.newInstance( connection = (Connection) constructor.newInstance(
"jdbc:h2:" + discordSRV.dataDirectory().resolve("h2-database").toAbsolutePath(), "jdbc:h2:" + discordSRV.dataDirectory().resolve("h2-database").toAbsolutePath(),
@ -80,4 +99,17 @@ public class H2Storage extends SQLStorage {
public boolean isAutoCloseConnections() { public boolean isAutoCloseConnections() {
return false; return false;
} }
@Override
public void createTables(Connection connection) throws SQLException {
try (Statement statement = connection.createStatement()) {
statement.execute(
"create table if not exists LINKED_ACCOUNTS "
+ "(ID int not null auto_increment, "
+ "PLAYER_UUID varchar(36), "
+ "USER_ID bigint, "
+ "constraint LINKED_ACCOUNTS_PK primary key (ID)"
+ ")");
}
}
} }

View File

@ -1,3 +1,21 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.storage.impl.sql.hikari; package com.discordsrv.common.storage.impl.sql.hikari;
import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.DiscordSRV;
@ -6,6 +24,7 @@ import com.discordsrv.common.exception.StorageException;
import com.discordsrv.common.storage.impl.sql.SQLStorage; import com.discordsrv.common.storage.impl.sql.SQLStorage;
import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource; import com.zaxxer.hikari.HikariDataSource;
import com.zaxxer.hikari.pool.HikariPool;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException; import java.sql.SQLException;
@ -21,7 +40,7 @@ public abstract class HikariStorage extends SQLStorage {
protected abstract void applyConfiguration(HikariConfig config, StorageConfig storageConfig); protected abstract void applyConfiguration(HikariConfig config, StorageConfig storageConfig);
protected <T extends ClassLoader> T initializeWithContext(T classLoader) { protected void initializeWithContext(ClassLoader classLoader) {
Thread currentThread = Thread.currentThread(); Thread currentThread = Thread.currentThread();
ClassLoader originalContext = currentThread.getContextClassLoader(); ClassLoader originalContext = currentThread.getContextClassLoader();
try { try {
@ -30,7 +49,6 @@ public abstract class HikariStorage extends SQLStorage {
} finally { } finally {
currentThread.setContextClassLoader(originalContext); currentThread.setContextClassLoader(originalContext);
} }
return classLoader;
} }
private void initializeInternal() { private void initializeInternal() {
@ -48,7 +66,16 @@ public abstract class HikariStorage extends SQLStorage {
config.setKeepaliveTime(poolConfig.keepaliveTime); config.setKeepaliveTime(poolConfig.keepaliveTime);
applyConfiguration(config, storageConfig); applyConfiguration(config, storageConfig);
hikariDataSource = new HikariDataSource(config); try {
hikariDataSource = new HikariDataSource(config);
} catch (RuntimeException e) {
// Avoid running into runtime ClassNotFoundException by not using this as the catch
if (e instanceof HikariPool.PoolInitializationException) {
// Already logged by Hikari, so we'll throw an empty exception
throw new StorageException((Throwable) null);
}
throw e;
}
super.initialize(); super.initialize();
} }

View File

@ -1,3 +1,21 @@
/*
* This file is part of DiscordSRV, licensed under the GPLv3 License
* Copyright (c) 2016-2022 Austin "Scarsz" Shapiro, Henri "Vankka" Schubin and DiscordSRV contributors
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.discordsrv.common.storage.impl.sql.hikari; package com.discordsrv.common.storage.impl.sql.hikari;
import com.discordsrv.common.DiscordSRV; import com.discordsrv.common.DiscordSRV;
@ -8,6 +26,9 @@ import com.zaxxer.hikari.HikariConfig;
import dev.vankka.dependencydownload.classloader.IsolatedClassLoader; import dev.vankka.dependencydownload.classloader.IsolatedClassLoader;
import java.io.IOException; import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map; import java.util.Map;
public class MySQLStorage extends HikariStorage { public class MySQLStorage extends HikariStorage {
@ -30,10 +51,23 @@ public class MySQLStorage extends HikariStorage {
} }
} }
@Override
public void createTables(Connection connection) throws SQLException {
try (Statement statement = connection.createStatement()) {
statement.execute(
"create table if not exists LINKED_ACCOUNTS "
+ "(ID int not null auto_increment, "
+ "PLAYER_UUID varchar(36), "
+ "USER_ID bigint, "
+ "constraint LINKED_ACCOUNTS_PK primary key (ID)"
+ ")");
}
}
@Override @Override
public void initialize() { public void initialize() {
try { try {
classLoader = initializeWithContext(DependencyLoader.mysql(discordSRV).loadIntoIsolated()); initializeWithContext(classLoader = DependencyLoader.mysql(discordSRV).loadIntoIsolated());
} catch (IOException e) { } catch (IOException e) {
throw new StorageException(e); throw new StorageException(e);
} }
@ -46,7 +80,7 @@ public class MySQLStorage extends HikariStorage {
address += ":3306"; address += ":3306";
} }
config.setDriverClassName("com.mysql.cj.jdbc.Driver"); config.setDriverClassName("com.mysql.cj.jdbc.NonRegisteringDriver");
config.setJdbcUrl("jdbc:mysql://" + address + "/" + storageConfig.remote.databaseName); config.setJdbcUrl("jdbc:mysql://" + address + "/" + storageConfig.remote.databaseName);
for (Map.Entry<Object, Object> entry : storageConfig.getDriverProperties().entrySet()) { for (Map.Entry<Object, Object> entry : storageConfig.getDriverProperties().entrySet()) {
config.addDataSourceProperty((String) entry.getKey(), entry.getValue()); config.addDataSourceProperty((String) entry.getKey(), entry.getValue());

View File

@ -30,6 +30,7 @@ import com.discordsrv.common.player.provider.AbstractPlayerProvider;
import com.discordsrv.common.plugin.PluginManager; import com.discordsrv.common.plugin.PluginManager;
import com.discordsrv.common.scheduler.Scheduler; import com.discordsrv.common.scheduler.Scheduler;
import com.discordsrv.common.scheduler.StandardScheduler; import com.discordsrv.common.scheduler.StandardScheduler;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.io.IOException; import java.io.IOException;
@ -96,6 +97,11 @@ public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionCon
return null; return null;
} }
@Override
public ClasspathAppender classpathAppender() {
return null;
}
@Override @Override
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() { public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
return null; return null;

View File

@ -29,6 +29,7 @@ import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.player.provider.AbstractPlayerProvider; import com.discordsrv.common.player.provider.AbstractPlayerProvider;
import com.discordsrv.common.plugin.PluginManager; import com.discordsrv.common.plugin.PluginManager;
import com.discordsrv.common.scheduler.Scheduler; import com.discordsrv.common.scheduler.Scheduler;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.nio.file.Path; import java.nio.file.Path;
@ -71,6 +72,11 @@ public class MockDiscordSRV extends AbstractDiscordSRV<MainConfig, ConnectionCon
return null; return null;
} }
@Override
public ClasspathAppender classpathAppender() {
return null;
}
@Override @Override
public @NotNull AbstractPlayerProvider<?, ?> playerProvider() { public @NotNull AbstractPlayerProvider<?, ?> playerProvider() {
return null; return null;

View File

@ -22,6 +22,7 @@ import com.discordsrv.common.dependency.InitialDependencyLoader;
import com.discordsrv.common.logging.Logger; import com.discordsrv.common.logging.Logger;
import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl; import com.discordsrv.common.logging.backend.impl.Log4JLoggerImpl;
import com.discordsrv.sponge.bootstrap.ISpongeBootstrap; import com.discordsrv.sponge.bootstrap.ISpongeBootstrap;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import dev.vankka.mcdependencydownload.bootstrap.AbstractBootstrap; import dev.vankka.mcdependencydownload.bootstrap.AbstractBootstrap;
import dev.vankka.mcdependencydownload.bootstrap.classpath.JarInJarClasspathAppender; import dev.vankka.mcdependencydownload.bootstrap.classpath.JarInJarClasspathAppender;
import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader; import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader;
@ -35,27 +36,27 @@ import java.nio.file.Path;
public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpongeBootstrap { public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpongeBootstrap {
private final Logger logger; private final Logger logger;
private final ClasspathAppender classpathAppender;
private final InitialDependencyLoader dependencies; private final InitialDependencyLoader dependencies;
private SpongeDiscordSRV discordSRV; private SpongeDiscordSRV discordSRV;
private final PluginContainer pluginContainer; private final PluginContainer pluginContainer;
private final Game game; private final Game game;
private final JarInJarClassLoader classLoader;
private final Path dataDirectory; private final Path dataDirectory;
public DiscordSRVSpongeBootstrap(PluginContainer pluginContainer, Game game, JarInJarClassLoader classLoader, Path dataDirectory) throws IOException { public DiscordSRVSpongeBootstrap(PluginContainer pluginContainer, Game game, JarInJarClassLoader classLoader, Path dataDirectory) throws IOException {
// Don't change these parameters // Don't change these parameters
super(classLoader); super(classLoader);
this.logger = new Log4JLoggerImpl(pluginContainer.logger()); this.logger = new Log4JLoggerImpl(pluginContainer.logger());
this.classpathAppender = new JarInJarClasspathAppender(classLoader);
this.dependencies = new InitialDependencyLoader( this.dependencies = new InitialDependencyLoader(
logger, logger,
dataDirectory, dataDirectory,
new String[] {"dependencies/runtimeDownload-sponge.txt"}, new String[] {"dependencies/runtimeDownload-sponge.txt"},
new JarInJarClasspathAppender(classLoader) classpathAppender
); );
this.pluginContainer = pluginContainer; this.pluginContainer = pluginContainer;
this.game = game; this.game = game;
this.classLoader = classLoader;
this.dataDirectory = dataDirectory; this.dataDirectory = dataDirectory;
} }
@ -66,7 +67,7 @@ public class DiscordSRVSpongeBootstrap extends AbstractBootstrap implements ISpo
@Override @Override
public void onStarted() { public void onStarted() {
dependencies.enable(() -> this.discordSRV = new SpongeDiscordSRV(logger, pluginContainer, game, classLoader, dataDirectory)); dependencies.enable(() -> this.discordSRV = new SpongeDiscordSRV(logger, classpathAppender, dataDirectory, pluginContainer, game));
if (discordSRV != null) { if (discordSRV != null) {
discordSRV.invokeServerStarted(); discordSRV.invokeServerStarted();
} }

View File

@ -31,7 +31,7 @@ import com.discordsrv.sponge.console.SpongeConsole;
import com.discordsrv.sponge.player.SpongePlayerProvider; import com.discordsrv.sponge.player.SpongePlayerProvider;
import com.discordsrv.sponge.plugin.SpongePluginManager; import com.discordsrv.sponge.plugin.SpongePluginManager;
import com.discordsrv.sponge.scheduler.SpongeScheduler; import com.discordsrv.sponge.scheduler.SpongeScheduler;
import dev.vankka.mcdependencydownload.classloader.JarInJarClassLoader; import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.spongepowered.api.Game; import org.spongepowered.api.Game;
import org.spongepowered.api.event.Listener; import org.spongepowered.api.event.Listener;
@ -46,17 +46,19 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
private final Game game; private final Game game;
private final Logger logger; private final Logger logger;
private final ClasspathAppender classpathAppender;
private final Path dataDirectory; private final Path dataDirectory;
private final SpongeScheduler scheduler; private final SpongeScheduler scheduler;
private final SpongeConsole console; private final SpongeConsole console;
private final SpongePlayerProvider playerProvider; private final SpongePlayerProvider playerProvider;
private final SpongePluginManager pluginManager; private final SpongePluginManager pluginManager;
public SpongeDiscordSRV(Logger logger, PluginContainer pluginContainer, Game game, JarInJarClassLoader classLoader, Path dataDirectory) { public SpongeDiscordSRV(Logger logger, ClasspathAppender classpathAppender, Path dataDirectory, PluginContainer pluginContainer, Game game) {
this.logger = logger;
this.classpathAppender = classpathAppender;
this.dataDirectory = dataDirectory;
this.pluginContainer = pluginContainer; this.pluginContainer = pluginContainer;
this.game = game; this.game = game;
this.logger = logger;
this.dataDirectory = dataDirectory;
this.scheduler = new SpongeScheduler(this); this.scheduler = new SpongeScheduler(this);
this.console = new SpongeConsole(this); this.console = new SpongeConsole(this);
@ -116,6 +118,11 @@ public class SpongeDiscordSRV extends ServerDiscordSRV<MainConfig, ConnectionCon
return OnlineMode.of(game.server().isOnlineModeEnabled()); return OnlineMode.of(game.server().isOnlineModeEnabled());
} }
@Override
public ClasspathAppender classpathAppender() {
return classpathAppender;
}
@Override @Override
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() { public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
return null; return null;

View File

@ -30,6 +30,7 @@ import com.velocitypowered.api.plugin.Plugin;
import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.plugin.annotation.DataDirectory; import com.velocitypowered.api.plugin.annotation.DataDirectory;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import dev.vankka.mcdependencydownload.velocity.classpath.VelocityClasspathAppender; import dev.vankka.mcdependencydownload.velocity.classpath.VelocityClasspathAppender;
import java.io.IOException; import java.io.IOException;
@ -46,6 +47,7 @@ import java.nio.file.Path;
public class DiscordSRVVelocityBootstrap { public class DiscordSRVVelocityBootstrap {
private final Logger logger; private final Logger logger;
private final ClasspathAppender classpathAppender;
private final InitialDependencyLoader dependencies; private final InitialDependencyLoader dependencies;
private final ProxyServer proxyServer; private final ProxyServer proxyServer;
private final PluginContainer pluginContainer; private final PluginContainer pluginContainer;
@ -55,11 +57,12 @@ public class DiscordSRVVelocityBootstrap {
@Inject @Inject
public DiscordSRVVelocityBootstrap(com.discordsrv.x.slf4j.Logger logger, ProxyServer proxyServer, PluginContainer pluginContainer, @DataDirectory Path dataDirectory) throws IOException { public DiscordSRVVelocityBootstrap(com.discordsrv.x.slf4j.Logger logger, ProxyServer proxyServer, PluginContainer pluginContainer, @DataDirectory Path dataDirectory) throws IOException {
this.logger = new SLF4JLoggerImpl(logger); this.logger = new SLF4JLoggerImpl(logger);
this.classpathAppender = new VelocityClasspathAppender(this, proxyServer);
this.dependencies = new InitialDependencyLoader( this.dependencies = new InitialDependencyLoader(
this.logger, this.logger,
dataDirectory, dataDirectory,
new String[] {"dependencies/runtimeDownload-velocity.txt"}, new String[] {"dependencies/runtimeDownload-velocity.txt"},
new VelocityClasspathAppender(this, proxyServer) classpathAppender
); );
this.proxyServer = proxyServer; this.proxyServer = proxyServer;
this.pluginContainer = pluginContainer; this.pluginContainer = pluginContainer;
@ -68,7 +71,7 @@ public class DiscordSRVVelocityBootstrap {
@Subscribe @Subscribe
public void onProxyInitialize(ProxyInitializeEvent event) { public void onProxyInitialize(ProxyInitializeEvent event) {
dependencies.loadAndEnable(() -> this.discordSRV = new VelocityDiscordSRV(this, logger, proxyServer, pluginContainer, dataDirectory)); dependencies.loadAndEnable(() -> this.discordSRV = new VelocityDiscordSRV(this, logger, classpathAppender, proxyServer, pluginContainer, dataDirectory));
} }
@Subscribe @Subscribe

View File

@ -32,6 +32,7 @@ import com.discordsrv.velocity.player.VelocityPlayerProvider;
import com.discordsrv.velocity.plugin.VelocityPluginManager; import com.discordsrv.velocity.plugin.VelocityPluginManager;
import com.velocitypowered.api.plugin.PluginContainer; import com.velocitypowered.api.plugin.PluginContainer;
import com.velocitypowered.api.proxy.ProxyServer; import com.velocitypowered.api.proxy.ProxyServer;
import dev.vankka.dependencydownload.classpath.ClasspathAppender;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.nio.file.Path; import java.nio.file.Path;
@ -43,17 +44,19 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
private final PluginContainer pluginContainer; private final PluginContainer pluginContainer;
private final Logger logger; private final Logger logger;
private final ClasspathAppender classpathAppender;
private final Path dataDirectory; private final Path dataDirectory;
private final StandardScheduler scheduler; private final StandardScheduler scheduler;
private final VelocityConsole console; private final VelocityConsole console;
private final VelocityPlayerProvider playerProvider; private final VelocityPlayerProvider playerProvider;
private final VelocityPluginManager pluginManager; private final VelocityPluginManager pluginManager;
public VelocityDiscordSRV(Object plugin, Logger logger, ProxyServer proxyServer, PluginContainer pluginContainer, Path dataDirectory) { public VelocityDiscordSRV(Object plugin, Logger logger, ClasspathAppender classpathAppender, ProxyServer proxyServer, PluginContainer pluginContainer, Path dataDirectory) {
this.plugin = plugin; this.plugin = plugin;
this.logger = logger;
this.classpathAppender = classpathAppender;
this.proxyServer = proxyServer; this.proxyServer = proxyServer;
this.pluginContainer = pluginContainer; this.pluginContainer = pluginContainer;
this.logger = logger;
this.dataDirectory = dataDirectory; this.dataDirectory = dataDirectory;
this.scheduler = new StandardScheduler(this); this.scheduler = new StandardScheduler(this);
@ -116,6 +119,11 @@ public class VelocityDiscordSRV extends ProxyDiscordSRV<MainConfig, ConnectionCo
return OnlineMode.of(proxy().getConfiguration().isOnlineMode()); return OnlineMode.of(proxy().getConfiguration().isOnlineMode());
} }
@Override
public ClasspathAppender classpathAppender() {
return classpathAppender;
}
@Override @Override
public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() { public ConnectionConfigManager<ConnectionConfig> connectionConfigManager() {
return null; return null;