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!
I can't explain the in-depth details but the main reason is that spigot uses own custom class loader to handle plugin dependencies.