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
This commit is contained in:
Aurora Lahtela 2022-07-15 08:21:23 +03:00
parent a82bcbff0d
commit b0be4f296d
16 changed files with 98 additions and 39 deletions

View File

@ -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"

View File

@ -23,5 +23,4 @@ shadowJar {
configurations = [project.configurations.shadow]
relocate 'org.bstats', 'net.playeranalytics.bstats.utilities.metrics'
relocate 'org.slf4j', 'plan.org.slf4j'
}

View File

@ -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.");
}

View File

@ -17,5 +17,4 @@ dependencies {
shadowJar {
configurations = [project.configurations.shadow]
relocate 'org.bstats', 'net.playeranalytics.bstats.utilities.metrics'
relocate 'org.slf4j', 'plan.org.slf4j'
}

View File

@ -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();

View File

@ -170,6 +170,4 @@ processResources {
shadowJar {
configurations = [project.configurations.shadow]
relocate 'org.slf4j', 'plan.org.slf4j'
}

View File

@ -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() {

View File

@ -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<SslContextFactory.Server> getSslContextFactory() {

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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> T performOperation(ClassLoader usingClassLoader, Supplier<T> 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);
}
}
}

View File

@ -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"

View File

@ -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();

View File

@ -19,5 +19,4 @@ dependencies {
shadowJar {
configurations = [project.configurations.shadow]
relocate 'org.slf4j', 'plan.org.slf4j'
}

View File

@ -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();

View File

@ -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

View File

@ -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();

View File

@ -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();