Class ClassLoaderFixer

  • All Implemented Interfaces:

    
    public final class ClassLoaderFixer
    
                        

    Methods to trick out the ClassLoader.

    The JavaPluginLoader removes the plugin's class loader from its List<PluginClassLoader> right after calling onDisable() and closes it. This leads to issues when classes are needed to be first loaded in onDisable() - for example, kotlin.collections.EmptyIterator is often needed for the first time in onDisable() when iterating over module's storages, registered listeners, etc.

    I am confused as to why this is causing issues, because onDisable() is called blocking and hence should complete before the classloader is actually killed - maybe it's only a Paper issue.

    I have also noticed that even using lambdas or other anonymous classes in onDisable() sometimes cause issues, for example the NamespacedStorage class's shutdown() method sometimes throws NoClassDefFoundError for its usages of forEach { }.

    Note that all this only sometimes causes problems. The following methods use some dirty tricks to make sure that the classloader is not removed from the list and closed before onDisable() completes, and it loads classes that are known to cause issues right during onEnable().

    • Nested Class Summary

      Nested Classes 
      Modifier and Type Class Description
    • Field Summary

      Fields 
      Modifier and Type Field Description
    • Enum Constant Summary

      Enum Constants 
      Enum Constant Description
    • Method Summary

      Modifier and Type Method Description
      final Unit trickOnEnable() Fixes NoClassDefFoundError when AbstractBasicsModule loops over storages in onDisable() when a module has not created any.
      final Unit setSuperEnabled(JavaPlugin plugin, Boolean enabled) Sets JavaPlugin#isEnabled.
      final Unit forceLoadClassesForEnclosingClass(Class<?> enclosingClass) Tries to force load all classes that are referenced in the given class.
      final Unit loadAllClassesInJar(String jarFilePath)
      • Methods inherited from class java.lang.Object

        clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
    • Method Detail

      • trickOnEnable

         final Unit trickOnEnable()

        Fixes NoClassDefFoundError when AbstractBasicsModule loops over storages in onDisable() when a module has not created any.

      • setSuperEnabled

         final Unit setSuperEnabled(JavaPlugin plugin, Boolean enabled)

        Sets JavaPlugin#isEnabled. Even though I checked Bukkit's classloading code, and as of 1.20.4 I didn't find any reference of isEnabled being checked there, I remember having seen it there somewhere in the findClass method. Maybe it's a Paper thing.

        The workaround is to set isEnabled to true in onDisable() and then set it back to false at the end of onDisable().

      • forceLoadClassesForEnclosingClass

         final Unit forceLoadClassesForEnclosingClass(Class<?> enclosingClass)

        Tries to force load all classes that are referenced in the given class. This is needed to avoid NoClassDefFoundErrors regarding lambdas and anonymous classes in CompletableFutures in onDisable().