Servlet, JSP, and Filter Basics

Introduction

Chapter 08 deployed a single doGet Servlet. Real web apps use the full Servlet lifecycle, POST bodies, sessions, Filters that run before Servlets, and sometimes JSP for HTML. This chapter explains those container-native APIs—what Tomcat calls before your business logic—and how they relate to Spring MVC and Spring Boot filters you may already know.

Prerequisites

Servlet Lifecycle

Tomcat creates one Servlet instance per class (default) and reuses it for many requests across threads.

Code explanation:

  • init() — read config, open expensive resources (use carefully—prefer lazy init or DI frameworks)
  • service() — dispatches to doGet, doPost, etc.
  • destroy() — release resources; not guaranteed on kill -9

Example with logging:

Check logs/catalina.*.log or console when redeploying—you should see init once per deploy.

doGet vs doPost

MethodTypical useRead body
GETRead-only, query paramsRarely
POSTForms, create/updateYes

Query parameters (GET or POST URL):

java
String name = req.getParameter("name"); // /hello/api?name=Ada

POST form fields (application/x-www-form-urlencoded):

java
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws IOException {
    req.setCharacterEncoding("UTF-8");
    String name = req.getParameter("name");
    resp.setContentType("text/plain; charset=UTF-8");
    resp.getWriter().println("Hello, " + name);
}

Test:

bash
curl -X POST http://127.0.0.1:8080/hello/api \
  -d "name=Tomcat"

JSON bodies — Servlets do not parse JSON automatically; read req.getInputStream() and use Jackson, or use Spring MVC @RequestBody. Container basics focus on streams and parameters.

Request and Response Objects

ObjectRole
HttpServletRequestMethod, headers, params, body, session access
HttpServletResponseStatus code, headers, writer/output stream
ServletContextApp-wide attributes (getServletContext())
HttpSessionPer-browser conversation state

ServletContext (application scope)

java
ServletContext ctx = getServletContext();
ctx.setAttribute("startupTime", System.currentTimeMillis());
Object val = ctx.getAttribute("startupTime");

Shared across all users and threads in this web app—use for read-mostly config, not user-specific data.

HttpSession (user scope)

java
HttpSession session = req.getSession(true);
session.setAttribute("user", "ada");
String user = (String) session.getAttribute("user");

Code explanation:

  • Session ID usually in cookie JSESSIONID
  • Default timeout from web.xml session-config (see web.xml chapter)
  • Prefer stateless JWT APIs in modern apps—sessions still appear in legacy JSP apps

Spring equivalent: @SessionAttribute, Spring Security context—not identical but similar “conversation” ideas.

JSP Basics

JSP (JavaServer Pages) embed HTML with Java snippets. Tomcat compiles .jsp files into Servlets under work/:

text
hello.jsp  →  work/Catalina/localhost/hello/org/apache/jsp/hello_jsp.java
           →  .class  →  executed like any Servlet

Add src/main/webapp/time.jsp:

jsp
<%@ page contentType="text/html; charset=UTF-8" %>
<!DOCTYPE html>
<html>
<body>
  <p>Server time: <%= new java.util.Date() %></p>
</body>
</html>

URL: http://localhost:8080/hello/time.jsp

Code explanation:

  • <%= … %> — expression output (learning only—avoid heavy logic in JSP)
  • First request may be slow (compile); later requests reuse class until file changes
  • Modern stacks prefer React/Vue static + JSON API, or Thymeleaf with Spring—JSP remains common in older codebases

JSP “changes not visible”

FixAction
Clear compile cacheStop Tomcat; delete work/Catalina/localhost/hello/; start
Confirm deployEdit correct webapps/hello/time.jsp, not stale exploded copy
ProductionPrecompile JSPs or avoid JSP; do not rely on reloadable

See Directory Layout — work/.

Filters

Filters wrap the request/response pipeline before and after Servlets—encoding, authentication, logging, CORS in simple apps.

text
Request → Filter1 → Filter2 → Servlet → Filter2 → Filter1 → Response

Example UTF-8 encoding filter:

Code explanation:

  • @WebFilter("/*") — all paths in this app
  • chain.doFilter — invoke next filter or target Servlet; skipping it blocks the request
  • Order: @WebFilter order or web.xml <filter-mapping> sequence

Register in web.xml instead (explicit order):

xml
<filter>
  <filter-name>EncodingFilter</filter-name>
  <filter-class>com.example.EncodingFilter</filter-class>
</filter>
<filter-mapping>
  <filter-name>EncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Spring Boot parallel: FilterRegistrationBean, Spring Security filter chain—higher level, same pipeline idea.

Listeners

ServletContextListener runs code when the app is deployed or undeployed:

Messages appear in logs/localhost.*.log.

Use cases: warm caches, register drivers (prefer container pools today), cleanup on undeploy.

Error Handling in Servlets

Set HTTP status and message:

java
resp.sendError(HttpServletResponse.SC_NOT_FOUND, "User not found");

Or forward to a JSP/HTML error page configured in web.xml error-page (see chapter 06).

Uncaught exceptions → 500; stack traces in logs/localhost.*.log, not always shown to clients (good for security).

Thread Safety

One Servlet instance, many threads. Do not store mutable request-specific state in instance fields:

java
// BAD — race between requests
private int counter;
 
// OK — local variables in doGet/doPost
int counter = 0;

Share read-only resources or use proper concurrent structures; prefer stateless handlers.

Servlet API vs Spring MVC (Quick Map)

Servlet APISpring MVC (Boot)
@WebServlet@Controller / @RestController
HttpServletRequest@RequestParam, HttpServletRequest injection
FilterFilter, HandlerInterceptor, Security filters
web.xmlapplication.yml, @Configuration
Manual JSON parsing@RequestBody, Jackson auto

Learning Servlets clarifies what Spring abstracts—useful when debugging WAR deploy on Tomcat without Boot magic.

Mini Lab: Extend hello.war

  1. Add LifecycleServlet, EncodingFilter, time.jsp
  2. mvn clean package, redeploy WAR
  3. Verify:
bash
curl http://127.0.0.1:8080/hello/lifecycle
curl http://127.0.0.1:8080/hello/time.jsp
curl -X POST -d "name=Ada" http://127.0.0.1:8080/hello/api
  1. Read logs/localhost.*.log for listener and filter activity

Common Issues

IssueCauseFix
POST body empty / wrong encodingMissing UTF-8 on requestEncodingFilter; req.setCharacterEncoding
Filter blocks all requestsNo chain.doFilterAdd call in doFilter
JSP compile errorSyntax in JSPFix JSP; read stack trace in log
Session always nullCookies disabled; wrong appBrowser cookies; correct context path
405 Method Not AllowedGET only Servlet hit with POSTImplement doPost or use GET

Post-Chapter Checklist

  • Draw lifecycle: init → service/doGet/doPost → destroy
  • Use HttpSession and ServletContext appropriately
  • Explain Filter chain and chain.doFilter
  • Know JSP compiles under work/
  • Relate Filters to Spring Security conceptually

What Comes Next

ChapterTopic
10 — Logging and access logAccessLogValve, log files
14 — Spring Boot WAR on TomcatFramework on same container
20 — TroubleshootingDeeper diagnostics

FAQ

One Servlet instance per request?

No—one instance per Servlet class (singleton default). Many concurrent requests share it—write thread-safe code.

JSP vs Servlet for HTML?

Historically JSP for HTML, Servlet for control. Today: static frontend + JSON API, or server templates (Thymeleaf). JSP maintenance still exists in legacy apps.

Filter vs Servlet?

Filter — cross-cutting pipeline wrapper, no direct URL target (unless chain ends early). Servlet — primary endpoint handler.

Where are sessions stored?

Tomcat default: in-memory on that node. Clustering/replication is advanced—not covered here.

Use @WebFilter and web.xml together?

Possible—know ordering rules. Prefer one style per app for clarity.

Replace Servlets with Spring Boot?

For new JSON APIs, usually yes. For learning Tomcat ops and maintaining WARs, Servlets remain relevant.