java.lang.NoClassDefFoundError for bundled class

I’m attempting to writing a simple HTTP API to query a server for information. I’m using spark to build the API and am bundling with gradle. I am running into the issue that, although javax.servlet-api is bundled in my jar, the Minecraft launch loader cannot find it:

mc         | java.lang.NoClassDefFoundError: javax/servlet/ServletContext
mc         | 	at org.eclipse.jetty.server.Server.doStart(Server.java:356) ~[Server.class:?]
mc         | 	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68) ~[AbstractLifeCycle.class:?]
mc         | 	at spark.embeddedserver.jetty.EmbeddedJettyServer.ignite(EmbeddedJettyServer.java:149) ~[EmbeddedJettyServer.class:?]
mc         | 	at spark.Service.lambda$init$2(Service.java:496) ~[Service.class:?]
mc         | 	at java.lang.Thread.run(Thread.java:748) [?:1.8.0_151]
mc         | Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletContext
mc         | 	at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:101) ~[launchwrapper-1.12.jar:?]
mc         | 	at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[?:1.8.0_151]
mc         | 	at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[?:1.8.0_151]
mc         | 	... 5 more
mc         | [04:54:15 ERROR] [STDERR]: Exception in thread "Thread-3" java.lang.NoClassDefFoundError: javax/servlet/ServletContext
mc         | [04:54:15 ERROR] [STDERR]: 	at org.eclipse.jetty.server.Server.doStart(Server.java:356)
mc         | [04:54:15 ERROR] [STDERR]: 	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:68)
mc         | [04:54:15 ERROR] [STDERR]: 	at spark.embeddedserver.jetty.EmbeddedJettyServer.ignite(EmbeddedJettyServer.java:149)
mc         | [04:54:15 ERROR] [STDERR]: 	at spark.Service.lambda$init$2(Service.java:496)
mc         | [04:54:15 ERROR] [STDERR]: 	at java.lang.Thread.run(Thread.java:748)
mc         | [04:54:15 ERROR] [STDERR]: Caused by: java.lang.ClassNotFoundException: javax.servlet.ServletContext
mc         | [04:54:15 ERROR] [STDERR]: 	at net.minecraft.launchwrapper.LaunchClassLoader.findClass(LaunchClassLoader.java:101)
mc         | [04:54:15 ERROR] [STDERR]: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
mc         | [04:54:15 ERROR] [STDERR]: 	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
mc         | [04:54:15 ERROR] [STDERR]: 	... 5 more

However, if I do the following before calling into Spark, everything loads just fine:

        ClassLoader loader = HttpApiPlugin.class.getClassLoader();
        try {
            loader.loadClass("javax/servlet/ServletContext");
        } catch (final ClassNotFoundException e) {}

Alternatively, relocating javax.servlet to ca.brennie.vendor.javax.servlet also results in a working plugin.

I have a minimum working example at this gist. I’d be happy for any help as to why this is happening. (I took a peek at the source for LaunchClassLoader and see that it excludes javax. classpaths from its transformations, but shrug). Would adding my own exclusion for javax.servlet work? Is that the correct way to handle this?

I had very similar issues with my plugin when trying to use the javax namespace. Although I’m not sure they’re related, and I honestly don’t remember what exactly wasn’t working.

Can you show us the code which triggers the error? I’m not familiar with spark, but does it autoload/search your classpath for a certain annotation, and then automatically register the found servlets? Or is it the import of the spark namespace that causes the error?

On a side note, depending on the functionality you’re looking for I already made a plugin which exposes an HTTP API, which might be enough for your use case - that being said, don’t let it stop you from making your own!

I linked a minimal example that causes the error in my first post.

No, Spark does not search the classpath for servlets. You register HTTP method handlers and middlewares and it (I assume) creates a servlet out of that definition.

I’ve found your plugin but it seems a bit too heavy for what I’m doing.