tl;dr
Your project lacks a JAR containing the definition of the Jakarta Servlet API (a set of interfaces).
Add jakarta.servlet-api
as a dependency to your project, to present to your IDE the needed JAR file during development but omitted from your final build’s WAR file.
Details
Your import
statements refer to the Jakarta Servlet API. That API is not built into Java. So you must make a Servlet API JAR file available to your IDE while developing.
Typically in Java development we use a tool such as Maven or Gradle to assist our IDE in obtaining external dependencies such as the Servlet API JAR.
If you are using Maven, edit your POM file to specify a dependency for the Servlet API JAR published as part of the Jakarta.ee project, by the Eclipse Foundation.
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
Notice the scope
element, with a value of provided
. This means the JAR file should be downloaded, made available to your IDE while developing, but should not be copied into your resulting product when you build.
The output of your build will typically be a WAR file (not JAR). The provided
scope means we do not want a copy of the Servlet API JAR within that WAR. The reason is that when developing Jakarta EE web apps, we expect to deploy to a compliant application server that carries its own copy of the Servlet API. Every “profile” of Jakarta EE compliant servers are required to include an implementation of the Servlet API.
So your POM will look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>work.basil.example</groupId>
<artifactId>demo</artifactId>
<version>1.0-SNAPSHOT</version>
<name>demo</name>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/jakarta.servlet/jakarta.servlet-api -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>3.3.2</version>
</plugin>
</plugins>
</build>
</project>
And here is the source code for a simple servlet.
package work.basil.example.demo;
import java.io.*;
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
@WebServlet ( name = "helloServlet", value = "/hello-servlet" )
public class HelloServlet extends HttpServlet
{
private String message;
public void init ( )
{
message = "Hello World!";
}
public void doGet ( HttpServletRequest request , HttpServletResponse response ) throws IOException
{
response.setContentType( "text/html" );
// Hello
PrintWriter out = response.getWriter();
out.println( "<html><body>" );
out.println( "<h1>" + message + "</h1>" );
out.println( "</body></html>" );
}
public void destroy ( )
{
}
}
You said:
I've tried uninstalling and reinstalling Tomcat several times
Completely unrelated to your problem, I expect.
The installation of Tomcat comes with an implementation of the Servlet API. But your IDE does not “see” Tomcat during development. Your IDE needs to see the interfaces defined in the Servlet API.
Your IDE does not actually execute your servlet, so it does not need an implementation, it needs only the interfaces. When you run your Servlet from the IDE, Tomcat is invoked externally from the IDE. The IDE attaches a debugger connection to Tomcat to facilitate interactive debugging, but the IDE and Tomcat are actually running as separate distinct processes on the host OS. And as I said, Tomcat comes bundled with its own implementation of those interfaces, so your servlet is able to execute.
You said you are using Java 13. Be aware that Java 13 is no longer supported. You should move to either the latest version of Java (17, as of 2021-11), or to one of the Long-Term Support (LTS) versions (8, 11, 17).
If using Java 11 or 17 rather than 8, I would suggest using Tomcat 10.1.x rather than Tomcat 10.0.x. Tomcat 10.1.x supports Jakarta EE 9.1 and 10, which supports Java 11 and later versus Java 8.
See the Tomcat version comparison page.