Skip to content

Instantly share code, notes, and snippets.

@RezzedUp
Last active February 29, 2024 19:32
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RezzedUp/d7957af10bfbfc6837ae1a4b55975f40 to your computer and use it in GitHub Desktop.
Save RezzedUp/d7957af10bfbfc6837ae1a4b55975f40 to your computer and use it in GitHub Desktop.
Guide - Using Javalin in a Spigot or Bungeecord Plugin

Using Javalin in a Spigot or Bungeecord Plugin

A common problem when attempting to use Javalin in a Spigot or Bungeecord plugin is the following:

[ERROR] java.lang.RuntimeException: javax.servlet.ServletException: java.lang.RuntimeException: Unable to load org.eclipse.jetty.websocket.server.WebSocketServerFactory
        ...
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.websocket.server.WebSocketServerFactory

This is incredibly frustrating because as far as you can tell, you've included all of Javalin's dependencies properly. In fact, you're probably reading this guide right now because you yourself fell victim to these pesky errors. Don't get too fraught, the solution is right under your nose!

You see, Spigot and Bungee (and Bukkit of course) do this funky thing where each and every plugin gets its own unique custom class loader, which unfortunately interferes with libraries like Javalin. The solution, as you might expect, is to temporarily swap class loaders with your plugin's class loader.

Like so:

public class JavalinTestPlugin extends JavaPlugin
{
    private Javalin app = null;
    
    @Override
    public void onEnable()
    {
        setupWebServer();
    }
    
    // Because Bukkit/Spigot/Bungeecord uses a custom class loader per plugin, Javalin is unable
    // to start unless it is instantiated using the plugin's own class loader.
    private void setupWebServer()
    {
        // Get the current class loader.
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        
        // Temporarily set this thread's class loader to the plugin's class loader.
        // Replace JavalinTestPlugin.class with your own plugin's class.
        Thread.currentThread().setContextClassLoader(JavalinTestPlugin.class.getClassLoader());
        
        // Instantiate the web server (which will now load using the plugin's class loader).
        app = Javalin.create().start(7001);
        app.get("/", context -> context.result("Yay Javalin works!"));
        
        // Put the original class loader back where it was.
        Thread.currentThread().setContextClassLoader(classLoader);
    }
}

Now Javalin works like a charm. Hooray!

@ResetPower
Copy link

It works! Thanks a lot!

@filipizydorczyk
Copy link

Is somone able to explain to me why does spigot need custom class loader?

@Kadeluxe
Copy link

Is somone able to explain to me why does spigot need custom class loader?

I can't explain the in-depth details but the main reason is that spigot uses own custom class loader to handle plugin dependencies.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment