Build one complete Java web flow
We will create the Maven web project from VS Code, deploy the generated JSP first, add a Servlet that forwards to another JSP, and finally build a student form handled by a Servlet with a separate result page.
Displays pages and HTML forms.
Handles requests and prepares data.
Displays the result prepared by the Servlet.
Java 21 · Maven · Tomcat 11 · Jakarta Servlet 6.1 · VS Code · Tomcat extension by Al-rimi
Verify Java, Maven, VS Code, and Tomcat
The commands are the same on Windows, macOS, and Linux once Java and Maven are installed and available on PATH.
java --version
javac --version
mvn -versionjava and javac should report Java 21.
mvn -version should also report that Maven is running with your Java 21 JDK.
Use a normal extracted Tomcat installation whose root contains bin, conf, lib, and webapps. Tomcat 11 requires Java 17 or newer, so Java 21 is suitable.
Create the web app from VS Code
Do not create the folder structure manually. Use VS Code's Maven command so students learn the normal IDE workflow.
- Open VS Code.
- Open the Command Palette with
Ctrl + Shift + Pon Windows/Linux orCmd + Shift + Pon macOS. - Run
Maven: Create Maven Project. - Select
maven-archetype-webapp. - Choose the archetype version offered by VS Code/Maven.
- Enter Group ID:
com.example. - Enter Artifact ID:
servlet-demo. - Choose the folder where you want the project created, then open that project in VS Code.
The webapp archetype already creates an index.jsp. We are going to deploy and test that generated page before creating any Servlet.
Configure Java 21 and WAR packaging
The generated project may contain older defaults. Replace or update the relevant settings so the application compiles with Java 21.
<?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>com.example</groupId>
<artifactId>servlet-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
</properties>
<dependencies>
<!-- We add the Servlet dependency in the next step. -->
</dependencies>
<build>
<finalName>servlet-demo</finalName>
</build>
</project>Creates a deployable Java web application.
Prevents old Java 7 compiler defaults from breaking the build.
Produces servlet-demo.war and gives us a predictable context path.
You do not need failOnMissingWebXml in this codelab. Keep the generated WEB-INF/web.xml in place.
Find dependencies instead of memorizing XML
Use Maven Repository to search for libraries and copy the Maven dependency snippet into pom.xml.
- Open MVNRepository.
- Search for
jakarta servlet api. - Open
jakarta.servlet » jakarta.servlet-api. - Select version
6.1.0for Tomcat 11. - Choose the Maven snippet and copy it.
- Paste it inside the
<dependencies>section ofpom.xml. - Add
<scope>provided</scope>because Tomcat supplies the Servlet API at runtime.
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.1.0</version>
<scope>provided</scope>
</dependency>provided?Your code needs the Servlet API to compile, but Tomcat already provides those classes when the application runs. We therefore do not need to package another copy inside the WAR.
Configure Tomcat by Al-rimi
We will let the extension build and deploy the Maven project instead of manually running mvn package and copying the WAR.
- Open Extensions in VS Code.
- Search for
Al-rimi.tomcatorTomcatby Al-rimi. - Install the extension.
- Add/configure your Tomcat installation. Select the Tomcat root folder containing
bin,conf,lib, andwebapps. - Configure Java Home if requested. Point it to your JDK 21 installation.
- Use port
8080unless you already have another service using it. - Use the Maven build strategy for this Maven project.
On Windows a Tomcat path may look like C:\apache-tomcat-11. On macOS/Linux it may look like ~/Developer/apache-tomcat-11. The exact location is not important; the folder structure is.
Update web.xml for Jakarta Servlet
Before deploying the application, update the deployment descriptor generated by the Maven webapp archetype. Older archetypes may generate an old Java EE web.xml, while Tomcat 11 uses Jakarta EE.
Open:
Replace its existing contents with:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
version="6.0">
<display-name>tomcat_test</display-name>
</web-app>The Maven webapp archetype can generate an older deployment descriptor that uses the previous Java EE schema. Our application uses jakarta.servlet and runs on Tomcat 11, so we update the descriptor to the Jakarta EE web application schema before deployment.
web.xml is the web application's deployment descriptor. It lives inside WEB-INF and can contain application-level web configuration such as Servlet mappings, filters, listeners, welcome files, error pages, and other settings. In this codelab we use annotations such as @WebServlet for Servlet mappings, so the descriptor can remain minimal.
<display-name> is a human-readable application name. It does not determine the URL context path. The deployed WAR name still determines the normal context path, such as /servlet-demo.
Deploy before writing any Servlet
This separates deployment problems from Servlet problems. First prove that Maven + Tomcat + VS Code are working together.
- Make sure
servlet-demois open as your VS Code workspace. - Use the Tomcat extension's deploy command/button for the current project.
- The extension should detect the Maven project, build it, and deploy it to Tomcat.
- Start Tomcat from the extension if it is not already running.
- Open the deployed application in the browser.
You should see the content from the generated src/main/webapp/index.jsp. This confirms that the project can already build and deploy successfully.
If index.jsp works now but a Servlet fails later, you know Tomcat itself and the basic deployment are not the problem.
Create a JSP for the Servlet to serve
Instead of making the Servlet print HTML with response.getWriter(), we will let a JSP render the page.
Create:
<!DOCTYPE html>
<html>
<head>
<title>Hello Servlet</title>
</head>
<body>
<h1>Hello from JSP!</h1>
<p>This page was opened through a Servlet.</p>
</body>
</html>Forward a request from Java to JSP
Create the Java source folder if the archetype did not generate it, then add the Servlet.
package com.example;
import java.io.IOException;
import jakarta.servlet.ServletException;
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
protected void doGet(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
request.getRequestDispatcher("/hello.jsp")
.forward(request, response);
}
}Understand the code
package com.example;com.example Java package.extends HttpServlet@WebServlet("/hello")/hello to this Servlet.doGet(...)HttpServletRequest requestHttpServletResponse responsegetRequestDispatcher("/hello.jsp")hello.jsp..forward(request, response)What makes /hello execute this Servlet?
Redeploy with the Tomcat extension
Use the same deploy command again. The extension handles Maven build + deployment for you.
- Save
HelloWorldServlet.javaandhello.jsp. - Deploy the project again using the Al-rimi Tomcat extension.
- Wait for the deployment to finish successfully.
- Open the Servlet URL.
The browser requests the Servlet.
Tomcat calls doGet().
The Servlet forwards to the JSP.
Create the student form JSP
The form will submit a POST request back to a Student Servlet.
Create:
<!DOCTYPE html>
<html>
<head>
<title>Student Form</title>
</head>
<body>
<h1>Student Registration</h1>
<form action="${pageContext.request.contextPath}/student" method="post">
<label>Name:</label>
<input type="text" name="name" required>
<br><br>
<label>Email:</label>
<input type="email" name="email" required>
<br><br>
<label>Course:</label>
<input type="text" name="course" required>
<br><br>
<button type="submit">Submit</button>
</form>
</body>
</html>${pageContext.request.contextPath} produces the application's current context, such as /servlet-demo. The form therefore remains correct even if the application name changes later.
Create a separate JSP for the result
The Servlet will put values into request attributes, then this JSP will display them using JSP Expression Language.
Create:
<!DOCTYPE html>
<html>
<head>
<title>Student Result</title>
</head>
<body>
<h1>Student Submitted Successfully</h1>
<p>Name: ${studentName}</p>
<p>Email: ${studentEmail}</p>
<p>Course: ${studentCourse}</p>
<a href="${pageContext.request.contextPath}/student">
Submit another student
</a>
</body>
</html>${studentName} asks JSP to find an attribute called studentName. We will create that attribute inside the Servlet.
Use one Servlet to show and process the form
doGet() displays the form. doPost() processes submitted data and forwards to the result page.
Create:
package com.example;
import java.io.IOException;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
@WebServlet("/student")
public class StudentServlet extends HttpServlet {
@Override
protected void doGet(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
request.getRequestDispatcher("/student-form.jsp")
.forward(request, response);
}
@Override
protected void doPost(
HttpServletRequest request,
HttpServletResponse response
) throws ServletException, IOException {
String name = request.getParameter("name");
String email = request.getParameter("email");
String course = request.getParameter("course");
request.setAttribute("studentName", name);
request.setAttribute("studentEmail", email);
request.setAttribute("studentCourse", course);
request.getRequestDispatcher("/result.jsp")
.forward(request, response);
}
}What changed?
Reads values submitted by HTML inputs. The argument must match the input's name.
Attaches Java-side data to the current request so the next JSP can display it.
| Method | Purpose in our app |
|---|---|
GET /student | Show student-form.jsp. |
POST /student | Read the submitted form and show result.jsp. |
Deploy and test the student workflow
The final application now demonstrates both a simple Servlet-to-JSP forward and a complete form-processing flow.
- Save all Java and JSP files.
- Deploy the project again using the Al-rimi Tomcat extension.
- Open
http://localhost:8080/servlet-demo/student. - Tomcat executes
StudentServlet.doGet(). - The Servlet forwards to
student-form.jsp. - Fill in Name, Email, and Course.
- Click Submit.
- The browser sends
POST /servlet-demo/student. - Tomcat executes
StudentServlet.doPost(). - The Servlet reads the parameters, creates request attributes, and forwards to
result.jsp.
Forwards to the form.
Submits POST data.
Reads data → forwards to result.jsp.
Optional challenge
- Add an
agefield to the form. - Read it using
request.getParameter("age"). - Create a
studentAgerequest attribute. - Display it on
result.jsp.
Completion checklist
- I created the Maven webapp through the VS Code Command Palette.
- I configured Java 21 and WAR packaging in
pom.xml. - I found and added Jakarta Servlet API from MVNRepository.
- I configured the Tomcat extension by Al-rimi.
- I deployed and tested the generated
index.jspbefore creating a Servlet. - I understand
@WebServlet,HttpServlet,doGet(), request, and response. - I forwarded from
HelloWorldServlettohello.jsp. - I built
student-form.jsp,StudentServlet, andresult.jsp. - I used
getParameter()for form values. - I used
setAttribute()to pass data from a Servlet to JSP.
Codelab complete! 🎉
You can now create, deploy, and trace a traditional Java web application using Maven, Tomcat, Servlets, and JSP.