Assumes the following:

  1. Java 17.

  2. NetBeans 13.

  3. Tomcat 10 added to NetBeans using Services > Servers > Add server…

In NetBeans: New project > Java with Maven > Web Application

Project Name: TomcatBasicDemo
Group Id: org.northcoder
Package: org.northcoder.tomcatbasicdemo

When presented with a choice of which Java EE version to use, just choose Java EE 7 Web. This is not what we will be using (it is very old) but it is (currently) the most current version available in the drop-down in NetBeans.

We will use the pom.xml file to specify Jakarta EE 9…

(Just as a side note: After finishing creation of the new project, you can go to Project properties > run > JAVA EE Version - and there you will see a drop-down which has “Jakarta EE 9 Web” in its list - but that does not actually appear to change the application, as far as I can tell.)

The created project will contain a POM using the old Java EE 7 dependency. We will use the following cleaned-up POM for our needs, which replaces the old Java EE dependency:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.northcoder</groupId>
    <artifactId>TomcatBasicDemo</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>TomcatBasicDemo</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>jakarta.platform</groupId>
            <artifactId>jakarta.jakartaee-web-api</artifactId>
            <version>9.1.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.10.1</version>
                <configuration>
                    <source>17</source>
                    <target>17</target>                   
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.2</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
        </plugins>
        <finalName>basic_demo</finalName>
    </build>

</project>

Note the scope of the dependency, to support compilation, without conflicting with Tomcat-provided libs:

1
<scope>provided</scope>

Note also the use of:

1
<failOnMissingWebXml>false</failOnMissingWebXml>

This means we do not need a web.xml file - instead we will use annotations in the code (just for this demo).

We use this:

1
<finalName>basic_demo</finalName>

which causes the final WAR to be basic_demo.war.

NetBeans creates a simple welcome page (index.html):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<!DOCTYPE html>
<html>
    <head>
        <title>Start Page</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body>
        <h1>Hello World!</h1>
    </body>
</html>

We will test with this before adding a servlet.

NetBeans also creates a generated context.xml file:

1
2
<?xml version="1.0" encoding="UTF-8"?>
<Context path="/TomcatBasicDemo"/>

This appears to be needed for NetBeans to handle integration with Tomcat.

When you build and run the application, it creates a custom context in a file called TomcatBasicDemo.xml in Tomcat’s /conf/Catalina/localhost/. The contents are:

1
2
<?xml version="1.0" encoding="UTF-8"?>
<Context docBase="c:\path\to\NetBeansProject\TomcatBasicDemo\target\basic_demo" path="/TomcatBasicDemo"/>

NetBeans uses a custom docbase into which it deploys the webapp, and does not use the main /webapps directory. That docbase is in the NetBeans project’s target directory.

When you run this webapp, you see the “Hello World!” message from the index.html file at:

http://localhost:8080/TomcatBasicDemo/

Now we add a basic servlet - in this case using the @WebServlet annotation:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet(urlPatterns = "/hello", displayName = "Demo")
public class HelloWorld extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {

        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Demo</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World Servlet</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

With this new servlet, the original “Hello World!” message is still displayed at the previously shown URL - the context root of the web application.

But now the servlet’s message “Hello World Servlet” is displayed here:

http://localhost:8080/TomcatBasicDemo/hello

Additional Notes

Tomcat supported API versions.

There you see that Tomcat 10.0.x supports servlet spec 5.0 (Jakarta Servlet 5.0), which is supported by Jakarta EE 9.

The Jakarta servlet specification (v5.0) can be found here: https://jakarta.ee/specifications/servlet/5.0/jakarta-servlet-spec-5.0.html.

In the above specification document, the @WebServlet annotation is described here.

You can also find useful notes (with examples) for the mapping rules between path patterns and servlets here.