From b0be4f296dd3256213e3b972c81bbe6e48ced565 Mon Sep 17 00:00:00 2001 From: Aurora Lahtela <24460436+AuroraLS3@users.noreply.github.com> Date: Fri, 15 Jul 2022 08:21:23 +0300 Subject: [PATCH] Use plugin classloader for thread context when initializing system This allows ServiceLoader to find slf4j-nop provider for Jetty preventing SLF4J "No provider" error Affects issues: - Fixed #2438 --- Plan/build.gradle | 2 +- Plan/bukkit/build.gradle | 1 - .../main/java/com/djrapitops/plan/Plan.java | 19 +++---- Plan/bungeecord/build.gradle | 1 - .../java/com/djrapitops/plan/PlanBungee.java | 3 +- Plan/common/build.gradle | 2 - .../java/com/djrapitops/plan/PlanSystem.java | 1 + .../webserver/http/JettyWebserver.java | 32 +++++------- .../java/ThreadContextClassLoaderSwap.java | 51 +++++++++++++++++++ Plan/fabric/build.gradle | 2 + .../net/playeranalytics/plan/PlanFabric.java | 3 +- Plan/nukkit/build.gradle | 1 - .../java/com/djrapitops/plan/PlanNukkit.java | 3 +- Plan/plugin/build.gradle | 8 +++ .../java/com/djrapitops/plan/PlanSponge.java | 5 +- .../com/djrapitops/plan/PlanVelocity.java | 3 +- 16 files changed, 98 insertions(+), 39 deletions(-) create mode 100644 Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThreadContextClassLoaderSwap.java diff --git a/Plan/build.gradle b/Plan/build.gradle index dc7bdff26..b76a6d74e 100644 --- a/Plan/build.gradle +++ b/Plan/build.gradle @@ -90,7 +90,7 @@ subprojects { mysqlVersion = "8.0.26" sqliteVersion = "3.36.0.3" hikariVersion = "5.0.1" - slf4jVersion = "1.7.36" + slf4jVersion = "2.0.0-alpha7" geoIpVersion = "3.0.1" gsonVersion = "2.9.0" dependencyDownloadVersion = "1.2.1" diff --git a/Plan/bukkit/build.gradle b/Plan/bukkit/build.gradle index 736898a1c..d48f29c88 100644 --- a/Plan/bukkit/build.gradle +++ b/Plan/bukkit/build.gradle @@ -23,5 +23,4 @@ shadowJar { configurations = [project.configurations.shadow] relocate 'org.bstats', 'net.playeranalytics.bstats.utilities.metrics' - relocate 'org.slf4j', 'plan.org.slf4j' } \ No newline at end of file diff --git a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java index e0d229fa2..bee3bf8cd 100644 --- a/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java +++ b/Plan/bukkit/src/main/java/com/djrapitops/plan/Plan.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.gathering.ServerShutdownSave; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plan.settings.theme.PlanColorScheme; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import net.playeranalytics.plugin.BukkitPlatformLayer; import net.playeranalytics.plugin.PlatformAbstractionLayer; import net.playeranalytics.plugin.scheduling.RunnableFactory; @@ -71,7 +72,7 @@ public class Plan extends JavaPlugin implements PlanPlugin { .server(getServer()) .build(); try { - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); serverShutdownSave = component.serverShutdownSave(); locale = system.getLocaleSystem().getLocale(); system.enable(); @@ -175,37 +176,37 @@ public class Plan extends JavaPlugin implements PlanPlugin { } /** - * @deprecated Deprecated due to use of APF Config + * @deprecated Deprecated due to use of custom Config */ @Override - @Deprecated + @Deprecated(since = "Config.java (2018)") public void reloadConfig() { throw new IllegalStateException("This method should be used on this plugin. Use onReload() instead"); } /** - * @deprecated Deprecated due to use of APF Config + * @deprecated Deprecated due to use of custom Config */ @Override - @Deprecated + @Deprecated(since = "Config.java (2018)") public FileConfiguration getConfig() { throw new IllegalStateException("This method should be used on this plugin. Use getMainConfig() instead"); } /** - * @deprecated Deprecated due to use of APF Config + * @deprecated Deprecated due to use of custom Config */ @Override - @Deprecated + @Deprecated(since = "Config.java (2018)") public void saveConfig() { throw new IllegalStateException("This method should be used on this plugin. Use getMainConfig().save() instead"); } /** - * @deprecated Deprecated due to use of APF Config + * @deprecated Deprecated due to use of custom Config */ @Override - @Deprecated + @Deprecated(since = "Config.java (2018)") public void saveDefaultConfig() { throw new IllegalStateException("This method should be used on this plugin."); } diff --git a/Plan/bungeecord/build.gradle b/Plan/bungeecord/build.gradle index 7ae944701..ca44b8486 100644 --- a/Plan/bungeecord/build.gradle +++ b/Plan/bungeecord/build.gradle @@ -17,5 +17,4 @@ dependencies { shadowJar { configurations = [project.configurations.shadow] relocate 'org.bstats', 'net.playeranalytics.bstats.utilities.metrics' - relocate 'org.slf4j', 'plan.org.slf4j' } \ No newline at end of file diff --git a/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java b/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java index b5e26eb54..e1d9066b5 100644 --- a/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java +++ b/Plan/bungeecord/src/main/java/com/djrapitops/plan/PlanBungee.java @@ -23,6 +23,7 @@ import com.djrapitops.plan.exceptions.EnableException; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plan.settings.theme.PlanColorScheme; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import net.md_5.bungee.api.plugin.Plugin; import net.playeranalytics.plugin.BungeePlatformLayer; import net.playeranalytics.plugin.PlatformAbstractionLayer; @@ -61,7 +62,7 @@ public class PlanBungee extends Plugin implements PlanPlugin { .abstractionLayer(abstractionLayer) .build(); try { - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); locale = system.getLocaleSystem().getLocale(); system.enable(); diff --git a/Plan/common/build.gradle b/Plan/common/build.gradle index d7881debc..6d4766e5d 100644 --- a/Plan/common/build.gradle +++ b/Plan/common/build.gradle @@ -170,6 +170,4 @@ processResources { shadowJar { configurations = [project.configurations.shadow] - - relocate 'org.slf4j', 'plan.org.slf4j' } diff --git a/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java b/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java index 58bb538fe..d88bd3806 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/PlanSystem.java @@ -152,6 +152,7 @@ public class PlanSystem implements SubSystem { /** * Enables only the systems that are required for {@link com.djrapitops.plan.commands.PlanCommand}. + * * @see #enableOtherThanCommands() */ public void enableForCommands() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/http/JettyWebserver.java b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/http/JettyWebserver.java index 6244e17e9..39f9ba5fb 100644 --- a/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/http/JettyWebserver.java +++ b/Plan/common/src/main/java/com/djrapitops/plan/delivery/webserver/http/JettyWebserver.java @@ -20,6 +20,7 @@ import com.djrapitops.plan.delivery.webserver.ResponseResolver; import com.djrapitops.plan.delivery.webserver.configuration.WebserverConfiguration; import com.djrapitops.plan.delivery.webserver.configuration.WebserverLogMessages; import com.djrapitops.plan.exceptions.EnableException; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import net.playeranalytics.plugin.server.PluginLogger; import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory; import org.eclipse.jetty.http2.server.HTTP2CServerConnectionFactory; @@ -129,24 +130,19 @@ public class JettyWebserver implements WebServer { } private ALPNServerConnectionFactory getAlpnServerConnectionFactory(String protocol) { - ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - try { - ClassLoader pluginClassLoader = getClass().getClassLoader(); - // Jetty uses Thread context classloader, so we need to change to plugin classloader where the ALPNProcessor is. - Thread.currentThread().setContextClassLoader(pluginClassLoader); - - Class.forName("org.eclipse.jetty.alpn.java.server.JDK9ServerALPNProcessor"); - // ALPN is protocol upgrade protocol required for upgrading http 1.1 connections to 2 - ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory("h2", "h2c", "http/1.1"); - alpn.setDefaultProtocol(protocol); - - return alpn; - } catch (ClassNotFoundException ignored) { - logger.warn("JDK9ServerALPNProcessor not found. ALPN is not available."); - return null; - } finally { - Thread.currentThread().setContextClassLoader(contextClassLoader); - } + ClassLoader pluginClassLoader = getClass().getClassLoader(); + return ThreadContextClassLoaderSwap.performOperation(pluginClassLoader, () -> { + try { + Class.forName("org.eclipse.jetty.alpn.java.server.JDK9ServerALPNProcessor"); + // ALPN is protocol upgrade protocol required for upgrading http 1.1 connections to 2 + ALPNServerConnectionFactory alpn = new ALPNServerConnectionFactory("h2", "h2c", "http/1.1"); + alpn.setDefaultProtocol(protocol); + return alpn; + } catch (ClassNotFoundException ignored) { + logger.warn("JDK9ServerALPNProcessor not found. ALPN is not available."); + return null; + } + }); } private Optional getSslContextFactory() { diff --git a/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThreadContextClassLoaderSwap.java b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThreadContextClassLoaderSwap.java new file mode 100644 index 000000000..50be12024 --- /dev/null +++ b/Plan/common/src/main/java/com/djrapitops/plan/utilities/java/ThreadContextClassLoaderSwap.java @@ -0,0 +1,51 @@ +/* + * This file is part of Player Analytics (Plan). + * + * Plan is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License v3 as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Plan 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with Plan. If not, see . + */ +package com.djrapitops.plan.utilities.java; + +import java.util.function.Supplier; + +public class ThreadContextClassLoaderSwap { + + private ThreadContextClassLoaderSwap() { + /* static method utility class */ + } + + public static void performOperation(ClassLoader usingClassLoader, Runnable operation) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + try { + // Jetty uses Thread context classloader, so we need to change to plugin classloader where the ALPNProcessor is. + Thread.currentThread().setContextClassLoader(usingClassLoader); + + operation.run(); + } finally { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } + } + + public static T performOperation(ClassLoader usingClassLoader, Supplier operation) { + ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + try { + // Jetty uses Thread context classloader, so we need to change to plugin classloader where the ALPNProcessor is. + Thread.currentThread().setContextClassLoader(usingClassLoader); + + return operation.get(); + } finally { + Thread.currentThread().setContextClassLoader(contextClassLoader); + } + } + +} diff --git a/Plan/fabric/build.gradle b/Plan/fabric/build.gradle index d2b771104..79b79fb6d 100644 --- a/Plan/fabric/build.gradle +++ b/Plan/fabric/build.gradle @@ -50,6 +50,8 @@ shadowJar { exclude "**/*.svg" exclude "**/*.psd" exclude "**/*.map" + exclude "LICENSE*.txt" + exclude "jetty-dir.css" exclude "**/module-info.class" exclude "module-info.class" diff --git a/Plan/fabric/src/main/java/net/playeranalytics/plan/PlanFabric.java b/Plan/fabric/src/main/java/net/playeranalytics/plan/PlanFabric.java index a6def72b1..41c93f4ec 100644 --- a/Plan/fabric/src/main/java/net/playeranalytics/plan/PlanFabric.java +++ b/Plan/fabric/src/main/java/net/playeranalytics/plan/PlanFabric.java @@ -25,6 +25,7 @@ import com.djrapitops.plan.gathering.ServerShutdownSave; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plan.settings.theme.PlanColorScheme; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import net.fabricmc.api.DedicatedServerModInitializer; import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; @@ -93,7 +94,7 @@ public class PlanFabric implements PlanPlugin, DedicatedServerModInitializer { .build(); try { - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); serverShutdownSave = component.serverShutdownSave(); locale = system.getLocaleSystem().getLocale(); system.enable(); diff --git a/Plan/nukkit/build.gradle b/Plan/nukkit/build.gradle index 57bb0e106..54c2851fa 100644 --- a/Plan/nukkit/build.gradle +++ b/Plan/nukkit/build.gradle @@ -19,5 +19,4 @@ dependencies { shadowJar { configurations = [project.configurations.shadow] - relocate 'org.slf4j', 'plan.org.slf4j' } \ No newline at end of file diff --git a/Plan/nukkit/src/main/java/com/djrapitops/plan/PlanNukkit.java b/Plan/nukkit/src/main/java/com/djrapitops/plan/PlanNukkit.java index 842c7f0f9..219f2083d 100644 --- a/Plan/nukkit/src/main/java/com/djrapitops/plan/PlanNukkit.java +++ b/Plan/nukkit/src/main/java/com/djrapitops/plan/PlanNukkit.java @@ -28,6 +28,7 @@ import com.djrapitops.plan.gathering.ServerShutdownSave; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plan.settings.theme.PlanColorScheme; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import com.djrapitops.plan.utilities.logging.ErrorContext; import net.playeranalytics.plugin.NukkitPlatformLayer; import net.playeranalytics.plugin.PlatformAbstractionLayer; @@ -75,7 +76,7 @@ public class PlanNukkit extends PluginBase implements PlanPlugin { .abstractionLayer(abstractionLayer) .build(); try { - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); serverShutdownSave = component.serverShutdownSave(); locale = system.getLocaleSystem().getLocale(); system.enable(); diff --git a/Plan/plugin/build.gradle b/Plan/plugin/build.gradle index f926379a1..2caa4a3e5 100644 --- a/Plan/plugin/build.gradle +++ b/Plan/plugin/build.gradle @@ -31,6 +31,8 @@ shadowJar { exclude "**/*.svg" exclude "**/*.psd" exclude "**/*.map" + exclude "LICENSE*.txt" + exclude "jetty-dir.css" exclude "**/module-info.class" exclude "module-info.class" @@ -56,6 +58,12 @@ shadowJar { exclude "jakarta/xml/**/*" exclude "javassist/**/*" + relocate('org.slf4j', 'plan.org.slf4j') { + exclude 'com.djrapitops.plan.PlanVelocity' + exclude 'net.playeranalytics.plugin.VelocityPlatformLayer' + exclude 'net.playeranalytics.plugin.server.VelocityPluginLogger' + } + relocate('org.apache', 'plan.org.apache') { exclude 'org/apache/logging/**' exclude 'org/apache/maven/**' // This needs to be unrelocated for Sponge diff --git a/Plan/sponge/src/main/java/com/djrapitops/plan/PlanSponge.java b/Plan/sponge/src/main/java/com/djrapitops/plan/PlanSponge.java index c1c6ade44..bbee02c2b 100644 --- a/Plan/sponge/src/main/java/com/djrapitops/plan/PlanSponge.java +++ b/Plan/sponge/src/main/java/com/djrapitops/plan/PlanSponge.java @@ -24,6 +24,7 @@ import com.djrapitops.plan.gathering.ServerShutdownSave; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plan.settings.theme.PlanColorScheme; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import net.playeranalytics.plugin.PlatformAbstractionLayer; import net.playeranalytics.plugin.SpongePlatformLayer; import net.playeranalytics.plugin.scheduling.RunnableFactory; @@ -104,7 +105,7 @@ public class PlanSponge implements PlanPlugin { catchStartupErrors(() -> { component = makeComponent(); - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); system.enableForCommands(); }); } @@ -120,7 +121,7 @@ public class PlanSponge implements PlanPlugin { if (!firstBoot) { // Reinitialize component & system component = makeComponent(); - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); } serverShutdownSave = component.serverShutdownSave(); diff --git a/Plan/velocity/src/main/java/com/djrapitops/plan/PlanVelocity.java b/Plan/velocity/src/main/java/com/djrapitops/plan/PlanVelocity.java index ac9348e7f..514d35173 100644 --- a/Plan/velocity/src/main/java/com/djrapitops/plan/PlanVelocity.java +++ b/Plan/velocity/src/main/java/com/djrapitops/plan/PlanVelocity.java @@ -23,6 +23,7 @@ import com.djrapitops.plan.exceptions.EnableException; import com.djrapitops.plan.settings.locale.Locale; import com.djrapitops.plan.settings.locale.lang.PluginLang; import com.djrapitops.plan.settings.theme.PlanColorScheme; +import com.djrapitops.plan.utilities.java.ThreadContextClassLoaderSwap; import com.velocitypowered.api.command.CommandManager; import com.velocitypowered.api.event.Subscribe; import com.velocitypowered.api.event.proxy.ProxyInitializeEvent; @@ -106,7 +107,7 @@ public class PlanVelocity implements PlanPlugin { .abstractionLayer(abstractionLayer) .build(); try { - system = component.system(); + system = ThreadContextClassLoaderSwap.performOperation(getClass().getClassLoader(), component::system); locale = system.getLocaleSystem().getLocale(); system.enable();