Integrating JSP, JSTL, and Servlets in Web Applications
Course: Advanced Java Training
Topic: Integrating JSP, JSTL, and Servlets in Web Applications
Course Link: Advanced Java Training Course
Demo Link: Register for a Course Demo
Academy Website: JustAcademy Official Website
1. Introduction
Java web applications use Servlets, JavaServer Pages (JSP), and the Jakarta Standard Tag Library (JSTL) to handle HTTP requests, execute application operations, and display dynamic content. These technologies work together to separate request processing, business logic, and presentation.
A Servlet receives a browser request, validates submitted data, invokes service classes, and prepares information for the response. JSP generates the HTML page, while JSTL provides reusable tags for displaying collections, checking conditions, formatting values, and presenting messages.
For example, an employee management application can use a Servlet to retrieve employee records, place them in a request attribute, and forward the request to a JSP. The JSP can use JSTL to display the employee information in a table.
2. Learning Objectives
- Understand the purpose of Servlets, JSP, and JSTL.
- Explain how these technologies work together in a Java web application.
- Implement the Model-View-Controller (MVC) architecture.
- Pass data from Servlets to JSP pages using request attributes.
- Use JSTL and Expression Language (EL) to render dynamic content.
- Understand request forwarding, redirects, and application scopes.
- Apply validation, error handling, and security best practices.
- Build and test a small integrated Java web application.
3. Understanding Servlets
A Servlet is a Java component that runs inside a Servlet container, such as Apache Tomcat. It handles HTTP requests and coordinates the processing needed to produce a response.
3.1 Responsibilities of a Servlet
- Receive HTTP requests through methods such as
doGet() and doPost().
- Read query parameters and submitted form data.
- Validate user input.
- Call service classes and data-access components.
- Store information in request attributes.
- Forward requests to JSP views or redirect clients to another URL.
- Handle appropriate HTTP status codes and application errors.
3.2 Example Servlet
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;
@WebServlet("/welcome")
public class WelcomeServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute("message",
"Welcome to the Java web application");
request.getRequestDispatcher(
"/WEB-INF/views/welcome.jsp"
).forward(request, response);
}
}
In this example, the Servlet creates a request attribute named message and forwards the request to a JSP. The JSP can access the attribute through Expression Language.
4. Understanding JSP
JavaServer Pages (JSP) is a server-side presentation technology used to generate dynamic HTML. A JSP page is translated and compiled into a Servlet by the web container.
4.1 Responsibilities of JSP
- Display application data.
- Generate HTML forms, tables, headings, and messages.
- Access scoped attributes through Expression Language.
- Use JSTL tags for conditions, iteration, and formatting.
- Include reusable page fragments such as headers and footers.
4.2 Example JSP
<%@ page contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome</title>
</head>
<body>
<h1>Welcome Page</h1>
<p><c:out value="${message}"/></p>
</body>
</html>
The expression ${message} reads the attribute supplied by the Servlet. The c:out tag outputs the value with XML/HTML escaping enabled by default.
5. Understanding JSTL
JSTL provides standard tags that reduce the need for Java scriptlets in JSP pages. It helps keep presentation code organized and easier to maintain.
| Tag |
Purpose |
Example Use |
c:out |
Outputs a value with escaping enabled by default |
Display a username |
c:if |
Conditionally renders content |
Show a success message |
c:choose |
Evaluates multiple conditional branches |
Display different account statuses |
c:forEach |
Iterates over collections or supported ranges |
Display employees in a table |
c:set |
Sets a scoped attribute |
Store a view-level value |
fmt:formatNumber |
Formats numeric values |
Display currency |
fmt:formatDate |
Formats date and time values |
Display a date |
fmt:message |
Retrieves localized messages |
Display translated text |
For Jakarta Tags 3.0, the common tag-library URIs are jakarta.tags.core and jakarta.tags.fmt. Older JSTL versions commonly use URIs such as http://java.sun.com/jsp/jstl/core and http://java.sun.com/jsp/jstl/fmt. Always use the URI and implementation compatible with the application's dependencies.
6. Comparing Servlets, JSP, and JSTL
| Feature |
Servlet |
JSP |
JSTL |
| Primary purpose |
Request handling and control |
Presentation |
Reusable view tags |
| Handles HTTP requests |
Yes |
Indirectly through its generated Servlet |
No |
| Displays HTML |
Can, but usually delegates to a view |
Yes |
Supports HTML rendering through tags |
| Business logic |
Coordinates calls to application services |
Should be kept minimal |
Not intended for business rules |
| Typical example |
EmployeeServlet |
employees.jsp |
c:forEach |
7. MVC Architecture
The Model-View-Controller (MVC) pattern separates an application's responsibilities into three parts.
- Model: Contains data and business operations, such as model classes, services, repositories, and DAOs.
- View: Displays information to the user, commonly using JSP and JSTL.
- Controller: Receives requests, invokes application operations, and selects the view. A Servlet commonly performs this role.
MVC prevents presentation code from becoming overloaded with business rules and makes each layer easier to test and maintain.
8. Complete Request-Response Workflow
- The browser sends an HTTP request to a Servlet URL mapping.
- The Servlet receives the request and reads any required parameters.
- The Servlet validates the input and invokes a service or model component.
- The service retrieves or updates the required data.
- The Servlet places the data into request attributes.
- The Servlet forwards the request to the appropriate JSP.
- The JSP uses EL and JSTL to generate the page.
- The container sends the generated HTML response to the browser.
Example: The browser requests /employees. The EmployeeServlet retrieves employee records and sets an employees request attribute. It forwards the request to /WEB-INF/views/employees.jsp, where JSTL renders the employee list.
9. Suggested Project Structure
EmployeeWebApp/
├── src/main/java/
│ └── com/example/
│ ├── model/
│ │ └── Employee.java
│ ├── service/
│ │ └── EmployeeService.java
│ └── web/
│ └── EmployeeServlet.java
├── src/main/webapp/
│ ├── index.jsp
│ ├── assets/
│ │ └── styles.css
│ └── WEB-INF/
│ ├── web.xml
│ └── views/
│ └── employees.jsp
└── pom.xml
Keeping JSP files under WEB-INF prevents direct browser access to those views. Users reach them through the controller, which can prepare the required data and perform access checks.
10. Creating the Employee Model
The Employee model represents an employee's details. Getter methods allow Expression Language to access its properties.
package com.example.model;
import java.math.BigDecimal;
public class Employee {
private final int id;
private final String name;
private final String department;
private final BigDecimal salary;
public Employee(int id, String name, String department,
BigDecimal salary) {
this.id = id;
this.name = name;
this.department = department;
this.salary = salary;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public String getDepartment() {
return department;
}
public BigDecimal getSalary() {
return salary;
}
}
For example, ${employee.name} accesses the employee's name through the JavaBean-style getter method.
11. Creating a Service Class
A service class separates application operations from the controller. The following example returns sample data so the integration can be tested without a database.
package com.example.service;
import com.example.model.Employee;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.List;
public class EmployeeService {
public List<Employee> findAll() {
return Arrays.asList(
new Employee(101, "Asha Sharma", "IT",
new BigDecimal("55000.00")),
new Employee(102, "Rahul Verma", "HR",
new BigDecimal("48000.00")),
new Employee(103, "Neha Patel", "Finance",
new BigDecimal("62000.00"))
);
}
}
In a production application, the service would typically retrieve data through a repository or DAO and apply the required business rules.
12. Creating the Servlet Controller
The controller obtains employee data, places it in request scope, and forwards the request to the JSP.
package com.example.web;
import com.example.service.EmployeeService;
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;
@WebServlet("/employees")
public class EmployeeServlet extends HttpServlet {
private EmployeeService employeeService;
@Override
public void init() {
employeeService = new EmployeeService();
}
@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setAttribute(
"employees",
employeeService.findAll()
);
request.setAttribute(
"pageTitle",
"Employee Directory"
);
request.getRequestDispatcher(
"/WEB-INF/views/employees.jsp"
).forward(request, response);
}
}
12.1 Important Statements
| Code |
Explanation |
@WebServlet("/employees") |
Maps the Servlet to the specified URL pattern. |
employeeService.findAll() |
Retrieves the employee collection. |
request.setAttribute() |
Stores data for the current request. |
getRequestDispatcher() |
Obtains a dispatcher for the JSP resource. |
forward(request, response) |
Transfers server-side processing to the JSP. |
Compatibility note: This code uses the jakarta.servlet namespace. Older Java EE applications may use javax.servlet. Choose a compatible Servlet API and server version; do not mix the two namespaces in the same application.
13. Creating the JSP View with JSTL
Create employees.jsp inside WEB-INF/views. This page uses JSTL for iteration, conditions, and currency formatting.
<%@ page contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<%@ taglib prefix="fmt" uri="jakarta.tags.fmt" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0">
<title><c:out value="${pageTitle}"/></title>
</head>
<body>
<h1><c:out value="${pageTitle}"/></h1>
<c:choose>
<c:when test="${not empty employees}">
<table border="1" cellpadding="8"
cellspacing="0">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Department</th>
<th>Salary</th>
</tr>
</thead>
<tbody>
<c:forEach var="employee"
items="${employees}">
<tr>
<td><c:out value="${employee.id}"/></td>
<td><c:out value="${employee.name}"/></td>
<td><c:out value="${employee.department}"/></td>
<td>
<fmt:formatNumber
value="${employee.salary}"
type="currency"
currencyCode="INR"/>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</c:when>
<c:otherwise>
<p>No employees were found.</p>
</c:otherwise>
</c:choose>
</body>
</html>
The JSP does not need to execute database queries or contain complex Java logic. It receives the data from the controller and focuses on presenting it.
14. Using Expression Language with JSTL
Expression Language (EL) and JSTL complement each other. EL accesses data, while JSTL controls how content is displayed.
| Feature |
Example |
Purpose |
| Read an attribute |
${pageTitle} |
Access a scoped value. |
| Display a property |
${employee.name} |
Read a bean property. |
| Display escaped output |
<c:out value="${employee.name}"/> |
Render text with default XML/HTML escaping. |
| Loop through data |
<c:forEach items="${employees}" var="employee"> |
Process each collection item. |
| Check a condition |
<c:if test="${not empty employees}"> |
Render content only when the condition is true. |
15. Understanding Data Scopes
Servlets and JSP pages can store data in several scopes. The correct scope depends on how long the data must remain available.
| Scope |
Lifetime |
Typical Use |
| Page |
During processing of a JSP page |
Temporary view values. |
| Request |
Current request and server-side forwards |
Data prepared for one response. |
| Session |
Across requests in a user session |
Short-lived user-specific state. |
| Application |
While the web application is running |
Shared application-wide information. |
Request scope is generally appropriate for data needed only to render one page. Avoid putting large collections or request-specific information into session or application scope unless the application's requirements justify it.
16. Forwarding Versus Redirecting
Forwarding and redirecting serve different purposes. A forward transfers processing on the server, whereas a redirect asks the browser to make another request.
| Aspect |
Forward |
Redirect |
| Execution |
Server-side dispatch |
Browser sends a new request |
| Request attributes |
Remain available to the forwarded resource |
Not automatically preserved |
| Browser URL |
Normally stays the same |
Changes to the destination URL |
| Common use |
Render a JSP with prepared data |
Navigate after form processing |
Example of a forward:
request.setAttribute("message", "Employee loaded");
request.getRequestDispatcher(
"/WEB-INF/views/employees.jsp"
).forward(request, response);
Example of a redirect:
response.sendRedirect(
request.getContextPath() + "/employees"
);
For a successful form submission that changes data, the Post/Redirect/Get pattern helps reduce accidental resubmission when a user refreshes the result page.
17. Processing Form Data
A JSP can display a form, while a Servlet processes the submitted data and returns a suitable result. Server-side validation is essential even if the form also uses browser-side validation.
17.1 JSP Form Example
<%@ page contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Department Search</title>
</head>
<body>
<h1>Search Employees</h1>
<form action="${pageContext.request.contextPath}/search-employees"
method="post">
<label for="department">Department</label>
<input type="text" id="department"
name="department" maxlength="80" required>
<button type="submit">Search</button>
</form>
</body>
</html>
17.2 Servlet Form Handler Example
@WebServlet("/search-employees")
public class EmployeeSearchServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
String department = request.getParameter("department");
if (department == null || department.isBlank()) {
request.setAttribute(
"errorMessage",
"Please enter a department."
);
request.getRequestDispatcher(
"/WEB-INF/views/search.jsp"
).forward(request, response);
return;
}
// Call a service to validate and search for employees.
// Use parameterized SQL queries if a database is involved.
request.setAttribute(
"message",
"Search submitted successfully."
);
request.getRequestDispatcher(
"/WEB-INF/views/search.jsp"
).forward(request, response);
}
}
This example illustrates validation and request forwarding. A complete application must implement the actual search operation and apply the relevant business rules.
18. Displaying Messages and Handling Errors
Controllers can set safe success or error messages as request attributes. JSP pages can display them with JSTL.
Servlet example:
request.setAttribute(
"errorMessage",
"Unable to load employee records. Please try again."
);
request.getRequestDispatcher(
"/WEB-INF/views/error.jsp"
).forward(request, response);
JSP example:
<%@ page contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="jakarta.tags.core" %>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Application Message</title>
</head>
<body>
<c:if test="${not empty errorMessage}">
<p role="alert">
<c:out value="${errorMessage}"/>
</p>
</c:if>
</body>
</html>
Technical details should be logged securely on the server. Avoid displaying stack traces, credentials, database details, or internal file paths to end users.
19. Web Application Configuration
Servlets can be registered with annotations such as @WebServlet or configured through WEB-INF/web.xml. An application may use both approaches when configured appropriately.
A simplified deployment descriptor can define a welcome page and a custom error page:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
version="6.0">
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/views/404.jsp</location>
</error-page>
</web-app>
The deployment descriptor version must match the Servlet platform supported by the container. Check that annotation scanning is enabled if the application relies on annotations for Servlet registration.
20. Dependencies and Runtime Requirements
A working application needs a compatible Servlet container, JSP support, and a JSTL implementation. The Servlet API is normally declared with a provided scope in Maven because the container supplies it at runtime.
Example Maven dependency for a compatible Servlet 6.0 environment:
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
This is an example rather than a complete dependency configuration. JSP and JSTL dependencies vary by container, platform version, and packaging strategy. Use compatible libraries and follow the selected server's documentation.
21. Testing the Integrated Application
Test each layer individually and then verify the complete request-response workflow.
- Controller testing: Check URL mappings, parameter handling, and selected views.
- Service testing: Verify business rules and data returned by the service.
- View testing: Check that the JSP displays populated and empty collections correctly.
- Integration testing: Deploy to a compatible container and test actual HTTP requests.
- Security testing: Verify authorization, output escaping, validation, and safe error handling.
Test cases should include multiple employees, an empty list, invalid form input, an unexpected service error, and special characters in displayed text.
22. Common Problems and Troubleshooting
| Problem |
Possible Cause |
Solution |
| 404 Not Found |
Incorrect URL mapping or context path |
Verify the Servlet mapping, application deployment, and context path. |
| JSTL tags are not recognized |
Missing implementation or incorrect tag-library URI |
Check the dependency and the URI required by the JSTL version. |
| JSP cannot find an attribute |
Attribute not set or incorrectly named |
Check the controller and match the attribute name exactly. |
| EL returns an empty value |
Null data or incorrect property name |
Check the model, getter methods, and attribute contents. |
| Class or linkage errors |
Incompatible dependencies or API namespaces |
Align server and library versions; avoid mixing javax and jakarta APIs. |
| Forward fails after response output |
Response already committed |
Forward before writing or flushing the response. |
| Submitted text is corrupted |
Inconsistent character encoding |
Use UTF-8 consistently and set request encoding before reading POST parameters. |
| Direct access to JSP fails |
JSP is stored under WEB-INF |
Access the view through its controller Servlet. |
| Unexpected HTML markup appears |
Output not encoded for the target context |
Use appropriate output encoding and safe rendering practices. |
23. Security Best Practices
- Validate all input on the server, even when browser-side validation is present.
- Use prepared statements or parameterized queries to prevent SQL injection.
- Use
c:out for ordinary HTML text output and apply context-appropriate encoding elsewhere.
- Check authorization before returning or modifying protected resources.
- Use CSRF protection for state-changing forms where applicable.
- Keep internal JSP views under
WEB-INF when direct access is not desired.
- Store only necessary user-specific information in the session.
- Keep business logic in service and model classes rather than JSP scriptlets.
- Log technical errors securely without exposing sensitive information to users.
- Use maintained, compatible Servlet, JSP, and JSTL dependencies.
24. Best Practices for Maintainable Applications
- Keep each Servlet focused on a clear controller responsibility.
- Separate business operations into service classes.
- Use model classes or data-transfer objects to represent application data.
- Use request attributes for values needed for a single page render.
- Prefer JSTL and EL instead of Java scriptlets in JSP pages.
- Use descriptive URL patterns and consistent naming conventions.
- Extract reusable view fragments, such as headers and navigation, when appropriate.
- Use Post/Redirect/Get after successful form submissions when suitable.
- Test with the same major platform versions used in deployment.
25. Practical Exercise
Task: Build an Employee Directory application using a Servlet, JSP, and JSTL.
Requirements
- Create an Employee model containing ID, name, department, and salary.
- Create an EmployeeService class that returns sample employee records.
- Map an EmployeeServlet to
/employees.
- Retrieve the employee list and store it in a request attribute.
- Forward the request to a JSP inside
WEB-INF/views.
- Use
c:forEach to display employee records in a table.
- Use
c:choose or c:if to display an empty-state message.
- Use
fmt:formatNumber to format salary values as currency.
- Create a search form and validate its submitted values in a Servlet.
- Test successful requests, empty results, and invalid input.
Expected Outcome
The application should display employee details in a table, format salary values, and show a helpful message when no records are available. The Servlet should control request processing, the service should supply data, and JSP/JSTL should handle presentation.
26. Interview Questions and Answers
Q1. Why are Servlets, JSP, and JSTL used together?
Answer: Servlets coordinate HTTP requests, JSP generates the presentation, and JSTL provides standard tags for common display operations. Together, they support a separation of responsibilities.
Q2. What is the role of a Servlet in MVC?
Answer: A Servlet commonly acts as the controller. It receives requests, validates input, calls application services, prepares attributes, and selects the next view.
Q3. Why should business logic be kept out of JSP?
Answer: Keeping business rules in Java service classes improves testability and maintainability and allows JSP pages to focus on displaying data.
Q4. What is JSTL?
Answer: JSTL provides standard tags for iteration, conditional processing, formatting, and other common JSP view tasks.
Q5. What is the difference between EL and JSTL?
Answer: EL accesses values and evaluates expressions, while JSTL provides tags for controlling how content is rendered.
Q6. What does request.setAttribute() do?
Answer: It stores an object in request scope so that the current request and server-side forwarded resource can access it.
Q7. What is the difference between forwarding and redirecting?
Answer: Forwarding transfers processing on the server. Redirecting instructs the browser to make a new request to another URL.
Q8. Why place JSP files under WEB-INF?
Answer: Files under WEB-INF are not directly accessible through normal client URLs, so requests can be routed through a controller that prepares data and performs access checks.
Q9. What does c:forEach do?
Answer: It iterates through a collection or another supported range and exposes each current item through a variable.
Q10. How can JSP output injection risks be reduced?
Answer: Use escaped output for ordinary HTML text, apply context-appropriate encoding, validate input, and enforce authorization. Parameterized queries are also important when interacting with databases.
Q11. Why might JSTL tags fail to load?
Answer: The JSTL implementation may be missing, the tag-library URI may be incorrect for the installed version, or incompatible dependencies may be present.
Q12. What is Post/Redirect/Get?
Answer: It is a pattern in which a successful POST is followed by a redirect to a GET endpoint, helping reduce accidental resubmission when the browser refreshes the page.
27. Quick Revision Checklist
- Servlets handle requests and coordinate processing.
- Service and model classes hold application operations and data.
- JSP renders dynamic HTML.
- JSTL provides tags for conditions, iteration, and formatting.
- EL accesses attributes and model properties.
- Request attributes are useful for passing data to a forwarded JSP.
- Forwarding and redirecting have different behavior.
- JSP files under
WEB-INF should be reached through server-side routing.
- Servlet, JSP, and JSTL versions must be compatible with the runtime environment.
- Validation, authorization, output encoding, and safe error handling are essential.
28. Conclusion
Integrating JSP, JSTL, and Servlets is a foundational approach to building traditional Java web applications. Servlets manage request processing, service and model classes handle application operations, and JSP/JSTL provide a structured presentation layer. Using these technologies with MVC and Expression Language makes applications easier to understand, test, and maintain.
Practice by building the Employee Directory example, passing data from a Servlet to a JSP, rendering collections with JSTL, and handling validation and empty results. These exercises develop the practical skills needed to work with existing Java web applications.
29. Reference Links