This is a follow up to this question. I don’t think it’s a duplicate because the accepted answer indicates that Jetty 11 doesn’t work with javax
servlets, but I’m asking why Jetty 11 doesn’t work with jakarta
servlets.
I have an example project here that uses Jetty 9 to deploy a local server, including a javax
servlet that uses the @WebServlet
annotation. This all works fine.
Now I’m trying to update this to use Jetty 11 and the Jakarta servlets API.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>io.happycoding</groupId> <artifactId>jetty-hello-world</artifactId> <version>1</version> <properties> <!-- Java 11 --> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jetty.version>11.0.5</jetty.version> <exec.mainClass>io.happycoding.ServerMain</exec.mainClass> </properties> <dependencies> <!-- Jakarta Servlets API --> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>5.0.0</version> <scope>provided</scope> </dependency> <!-- Jetty --> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> <version>${jetty.version}</version> </dependency> <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-annotations</artifactId> <version>${jetty.version}</version> </dependency> </dependencies> <build> <plugins> <!-- Copy static resources like html files into the output jar file. --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-resources-plugin</artifactId> <version>2.7</version> <executions> <execution> <id>copy-web-resources</id> <phase>compile</phase> <goals><goal>copy-resources</goal></goals> <configuration> <outputDirectory> ${project.build.directory}/classes/META-INF/resources </outputDirectory> <resources> <resource><directory>./src/main/webapp</directory></resource> </resources> </configuration> </execution> </executions> </plugin> <!-- Package everything into a single executable jar file. --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-shade-plugin</artifactId> <version>3.2.4</version> <executions> <execution> <phase>package</phase> <goals><goal>shade</goal></goals> <configuration> <createDependencyReducedPom>false</createDependencyReducedPom> <transformers> <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> <mainClass>${exec.mainClass}</mainClass> </transformer> </transformers> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
ServerMain.java
package io.happycoding; import java.net.URL; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.webapp.WebInfConfiguration; /** * Starts up the server, including a DefaultServlet that handles static files, * and any servlet classes annotated with the @WebServlet annotation. */ public class ServerMain { public static void main(String[] args) throws Exception { // Create a server that listens on port 8080. Server server = new Server(8080); WebAppContext webAppContext = new WebAppContext(); server.setHandler(webAppContext); // Load static content from inside the jar file. URL webAppDir = ServerMain.class.getClassLoader().getResource("META-INF/resources"); webAppContext.setResourceBase(webAppDir.toURI().toString()); // Enable annotations so the server sees classes annotated with @WebServlet. webAppContext.setConfigurations(new Configuration[]{ new AnnotationConfiguration(), new WebInfConfiguration(), }); // Look for annotations in the classes directory (dev server) and in the // jar file (live server) webAppContext.setAttribute( "org.eclipse.jetty.server.webapp.ContainerIncludeJarPattern", ".*/target/classes/|.*\.jar"); // Handle static resources, e.g. html files. webAppContext.addServlet(DefaultServlet.class, "/"); // Start the server! 🚀 server.start(); System.out.println("Server started!"); // Keep the main thread alive while the server is running. server.join(); } }
HelloWorldServlet.java
package io.happycoding.servlets; import java.io.IOException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet("/hello") public class HelloWorldServlet extends HttpServlet { @Override public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { response.setContentType("text/html;"); response.getWriter().println("<h1>Hello world!</h1>"); } }
index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Jetty Hello World</title> </head> <body> <h1>Jetty Hello World</h1> <p>This is a sample HTML file. Click <a href="/hello">here</a> to see content served from a servlet.</p> <p>Learn more at <a href="https://happycoding.io">HappyCoding.io</a>.</p> </body> </html>
I can run the server using mvn package exec:java
and it starts up just fine.
My index.html
file loads properly:
But when I navigate to my servlet’s URL, I get a 404:
I don’t see any errors (or any output at all) in my command line.
I should also mention that my Jakarta servlet works fine with the full version of Jetty 11 (using Jetty home and Jetty base directories, as described here). It’s just this “standalone” Jetty server launcher that seems to be having trouble, so I’m suspicious of my ServerMain.java
file.
What do I need to change to get Jetty 11 to detect my Jakarta servlet?
Advertisement
Answer
Remove the configuration effort, it’s used wrong, and is the source of your issues.
These lines, delete them.
// Enable annotations so the server sees classes annotated with @WebServlet. webAppContext.setConfigurations(new Configuration[]{ new AnnotationConfiguration(), new WebInfConfiguration(), });
Starting with Jetty 10, you don’t manage Configuration
options this way.
The existence of the jar with the feature in your classpath is enough. (in your case it’s jetty-annotations-<ver>.jar
)
Second, you don’t ever use setConfigurations()
with that small of a Configuration
list (you are leaving out many required Configuration
entries).
Instead, you should have used the addConfiguration(Configuration...)
method, for just the new AnnotationConfiguration()
, but only if that specific Configuration
wasn’t auto-discoverable, which jetty-annotation
is.
Tip, use server.setDumpAfterStart(true)
before your server.start()
call, and see what the server state is, including what your WebAppContext
looks like, and its Configuration
entries. This is a great way to see what your changes are doing to your server and your contexts.
Here’s an example project with Jetty 11, using @WebServlet
, and WebAppContext
.