1. java
  2. /enterprise
  3. /servlets

Java Servlets and Web Application Development

Java Servlets

Java Servlets are server-side Java programs that handle client requests and generate dynamic web content. They form the foundation of Java web applications and provide a powerful, portable way to extend web server functionality.

Table of Contents

Servlet Fundamentals

What are Servlets?

Servlets are Java classes that extend the capabilities of servers that host applications accessed by way of a request-response programming model. They're primarily used to:

  • Process client requests dynamically
  • Generate dynamic web content
  • Manage sessions and state
  • Interact with databases and other resources

Basic Servlet Structure

import jakarta.servlet.*;
import jakarta.servlet.http.*;
import java.io.*;

public class HelloServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
            throws ServletException, IOException {
        
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        
        out.println("<html>");
        out.println("<head><title>Hello Servlet</title></head>");
        out.println("<body>");
        out.println("<h1>Hello, World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

Servlet API

Core Interfaces and Classes

HttpServlet Class

public class UserServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // Handle GET requests
        handleGetRequest(req, resp);
    }
    
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // Handle POST requests
        handlePostRequest(req, resp);
    }
    
    @Override
    protected void doPut(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // Handle PUT requests
        handlePutRequest(req, resp);
    }
    
    @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // Handle DELETE requests
        handleDeleteRequest(req, resp);
    }
    
    private void handleGetRequest(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        resp.setContentType("application/json");
        resp.getWriter().write("{\"message\": \"User data retrieved\"}");
    }
    
    private void handlePostRequest(HttpServletRequest req, HttpServletResponse resp) 
            throws IOException {
        resp.setContentType("application/json");
        resp.setStatus(HttpServletResponse.SC_CREATED);
        resp.getWriter().write("{\"message\": \"User created\"}");
    }
}

ServletRequest and ServletResponse

public class RequestResponseExample extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
            throws ServletException, IOException {
        
        // Reading request parameters
        String username = request.getParameter("username");
        String email = request.getParameter("email");
        String[] hobbies = request.getParameterValues("hobbies");
        
        // Reading request headers
        String userAgent = request.getHeader("User-Agent");
        String contentType = request.getContentType();
        
        // Reading request body (for JSON/XML)
        StringBuilder requestBody = new StringBuilder();
        try (BufferedReader reader = request.getReader()) {
            String line;
            while ((line = reader.readLine()) != null) {
                requestBody.append(line);
            }
        }
        
        // Setting response properties
        response.setContentType("application/json");
        response.setCharacterEncoding("UTF-8");
        response.setStatus(HttpServletResponse.SC_OK);
        
        // Adding response headers
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.addCookie(new Cookie("lastVisit", 
                          String.valueOf(System.currentTimeMillis())));
        
        // Writing response
        PrintWriter out = response.getWriter();
        out.println("{");
        out.println("  \"status\": \"success\",");
        out.println("  \"username\": \"" + username + "\",");
        out.println("  \"email\": \"" + email + "\"");
        out.println("}");
    }
}

Servlet Lifecycle

Lifecycle Methods

public class LifecycleServlet extends HttpServlet {
    
    private int accessCount = 0;
    
    @Override
    public void init() throws ServletException {
        // Called once when servlet is first loaded
        System.out.println("Servlet initialized");
        
        // Initialize resources
        initializeDatabase();
        loadConfiguration();
    }
    
    @Override
    public void init(ServletConfig config) throws ServletException {
        // Called with servlet configuration
        super.init(config);
        
        String dbUrl = config.getInitParameter("database.url");
        String dbUser = config.getInitParameter("database.user");
        
        System.out.println("Database URL: " + dbUrl);
    }
    
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) 
            throws ServletException, IOException {
        // Called for each request
        accessCount++;
        System.out.println("Request #" + accessCount);
        
        // Delegate to appropriate method (doGet, doPost, etc.)
        super.service(req, resp);
    }
    
    @Override
    public void destroy() {
        // Called when servlet is unloaded
        System.out.println("Servlet destroyed. Total requests: " + accessCount);
        
        // Cleanup resources
        closeDatabase();
        saveStatistics();
    }
    
    private void initializeDatabase() {
        // Database initialization logic
    }
    
    private void loadConfiguration() {
        // Configuration loading logic
    }
    
    private void closeDatabase() {
        // Database cleanup logic
    }
    
    private void saveStatistics() {
        // Statistics saving logic
    }
}

Request and Response Handling

Processing Form Data

@WebServlet("/user-registration")
public class UserRegistrationServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
            throws ServletException, IOException {
        
        // Set request encoding for international characters
        request.setCharacterEncoding("UTF-8");
        
        // Extract form data
        String firstName = request.getParameter("firstName");
        String lastName = request.getParameter("lastName");
        String email = request.getParameter("email");
        String password = request.getParameter("password");
        String birthDate = request.getParameter("birthDate");
        String[] interests = request.getParameterValues("interests");
        
        // Validation
        List<String> errors = validateUserData(firstName, lastName, email, password);
        
        if (!errors.isEmpty()) {
            // Forward to error page with validation errors
            request.setAttribute("errors", errors);
            request.setAttribute("formData", createFormDataMap(request));
            RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/error.jsp");
            dispatcher.forward(request, response);
            return;
        }
        
        try {
            // Create user object
            User user = new User();
            user.setFirstName(firstName);
            user.setLastName(lastName);
            user.setEmail(email);
            user.setPassword(hashPassword(password));
            user.setBirthDate(LocalDate.parse(birthDate));
            user.setInterests(Arrays.asList(interests != null ? interests : new String[0]));
            
            // Save to database
            UserService userService = new UserService();
            userService.createUser(user);
            
            // Redirect to success page
            response.sendRedirect(request.getContextPath() + "/registration-success");
            
        } catch (Exception e) {
            // Handle registration error
            request.setAttribute("error", "Registration failed: " + e.getMessage());
            RequestDispatcher dispatcher = request.getRequestDispatcher("/WEB-INF/error.jsp");
            dispatcher.forward(request, response);
        }
    }
    
    private List<String> validateUserData(String firstName, String lastName, 
                                         String email, String password) {
        List<String> errors = new ArrayList<>();
        
        if (firstName == null || firstName.trim().isEmpty()) {
            errors.add("First name is required");
        }
        if (lastName == null || lastName.trim().isEmpty()) {
            errors.add("Last name is required");
        }
        if (email == null || !isValidEmail(email)) {
            errors.add("Valid email is required");
        }
        if (password == null || password.length() < 8) {
            errors.add("Password must be at least 8 characters");
        }
        
        return errors;
    }
    
    private boolean isValidEmail(String email) {
        return email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
    }
    
    private String hashPassword(String password) {
        // Implement proper password hashing
        return password; // Simplified for example
    }
    
    private Map<String, String> createFormDataMap(HttpServletRequest request) {
        Map<String, String> formData = new HashMap<>();
        request.getParameterMap().forEach((key, values) -> {
            if (values.length > 0) {
                formData.put(key, values[0]);
            }
        });
        return formData;
    }
}

File Upload Handling

@WebServlet("/file-upload")
@MultipartConfig(
    fileSizeThreshold = 1024 * 1024 * 2,  // 2MB
    maxFileSize = 1024 * 1024 * 10,       // 10MB
    maxRequestSize = 1024 * 1024 * 50     // 50MB
)
public class FileUploadServlet extends HttpServlet {
    
    private static final String UPLOAD_DIR = "uploads";
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
            throws ServletException, IOException {
        
        // Get the absolute path of the web application
        String applicationPath = request.getServletContext().getRealPath("");
        String uploadFilePath = applicationPath + File.separator + UPLOAD_DIR;
        
        // Create the upload directory if it doesn't exist
        File uploadDir = new File(uploadFilePath);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        
        try {
            // Process each uploaded file
            for (Part part : request.getParts()) {
                String fileName = getFileName(part);
                
                if (fileName != null && !fileName.isEmpty()) {
                    // Validate file type
                    if (!isValidFileType(fileName)) {
                        response.getWriter().println("Invalid file type: " + fileName);
                        continue;
                    }
                    
                    // Generate unique filename to prevent conflicts
                    String uniqueFileName = generateUniqueFileName(fileName);
                    String filePath = uploadFilePath + File.separator + uniqueFileName;
                    
                    // Save the file
                    part.write(filePath);
                    
                    // Log file upload
                    System.out.println("File uploaded: " + uniqueFileName);
                    
                    // You could also save file metadata to database here
                    saveFileMetadata(uniqueFileName, part.getSize(), part.getContentType());
                }
            }
            
            response.getWriter().println("Files uploaded successfully!");
            
        } catch (Exception e) {
            response.getWriter().println("Error uploading files: " + e.getMessage());
        }
    }
    
    private String getFileName(Part part) {
        String contentDisposition = part.getHeader("content-disposition");
        String[] tokens = contentDisposition.split(";");
        
        for (String token : tokens) {
            if (token.trim().startsWith("filename")) {
                return token.substring(token.indexOf("=") + 2, token.length() - 1);
            }
        }
        return null;
    }
    
    private boolean isValidFileType(String fileName) {
        String[] allowedExtensions = {".jpg", ".jpeg", ".png", ".gif", ".pdf", ".doc", ".docx"};
        String lowerCaseFileName = fileName.toLowerCase();
        
        for (String ext : allowedExtensions) {
            if (lowerCaseFileName.endsWith(ext)) {
                return true;
            }
        }
        return false;
    }
    
    private String generateUniqueFileName(String originalFileName) {
        String timestamp = String.valueOf(System.currentTimeMillis());
        String extension = originalFileName.substring(originalFileName.lastIndexOf("."));
        return timestamp + "_" + originalFileName.replaceAll("[^a-zA-Z0-9\\.\\-]", "_");
    }
    
    private void saveFileMetadata(String fileName, long size, String contentType) {
        // Save file metadata to database
        // This would typically involve a DAO or service class
    }
}

Session Management

Session Handling

@WebServlet("/session-demo")
public class SessionDemoServlet extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
            throws ServletException, IOException {
        
        HttpSession session = request.getSession();
        
        // Get session information
        String sessionId = session.getId();
        long creationTime = session.getCreationTime();
        long lastAccessTime = session.getLastAccessedTime();
        int maxInactiveInterval = session.getMaxInactiveInterval();
        
        // Session attributes
        Integer visitCount = (Integer) session.getAttribute("visitCount");
        if (visitCount == null) {
            visitCount = 1;
        } else {
            visitCount++;
        }
        session.setAttribute("visitCount", visitCount);
        
        // Set session timeout (in seconds)
        session.setMaxInactiveInterval(30 * 60); // 30 minutes
        
        // Response
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        
        out.println("<html><body>");
        out.println("<h2>Session Information</h2>");
        out.println("<p>Session ID: " + sessionId + "</p>");
        out.println("<p>Creation Time: " + new Date(creationTime) + "</p>");
        out.println("<p>Last Access Time: " + new Date(lastAccessTime) + "</p>");
        out.println("<p>Max Inactive Interval: " + maxInactiveInterval + " seconds</p>");
        out.println("<p>Visit Count: " + visitCount + "</p>");
        out.println("<a href='" + request.getRequestURI() + "'>Refresh</a>");
        out.println("</body></html>");
    }
}

@WebServlet("/shopping-cart")
public class ShoppingCartServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
            throws ServletException, IOException {
        
        String action = request.getParameter("action");
        String productId = request.getParameter("productId");
        
        HttpSession session = request.getSession();
        
        // Get or create shopping cart
        @SuppressWarnings("unchecked")
        Map<String, CartItem> cart = (Map<String, CartItem>) session.getAttribute("cart");
        if (cart == null) {
            cart = new HashMap<>();
            session.setAttribute("cart", cart);
        }
        
        switch (action) {
            case "add":
                addToCart(cart, productId, request);
                break;
            case "remove":
                removeFromCart(cart, productId);
                break;
            case "clear":
                cart.clear();
                break;
        }
        
        // Calculate totals
        double total = cart.values().stream()
            .mapToDouble(item -> item.getPrice() * item.getQuantity())
            .sum();
        
        session.setAttribute("cartTotal", total);
        session.setAttribute("cartItemCount", cart.size());
        
        // Redirect back to shopping page
        response.sendRedirect(request.getContextPath() + "/shop");
    }
    
    private void addToCart(Map<String, CartItem> cart, String productId, 
                          HttpServletRequest request) {
        if (cart.containsKey(productId)) {
            CartItem item = cart.get(productId);
            item.setQuantity(item.getQuantity() + 1);
        } else {
            // In real application, you'd fetch product details from database
            String productName = request.getParameter("productName");
            double price = Double.parseDouble(request.getParameter("price"));
            
            CartItem item = new CartItem(productId, productName, price, 1);
            cart.put(productId, item);
        }
    }
    
    private void removeFromCart(Map<String, CartItem> cart, String productId) {
        cart.remove(productId);
    }
}

Servlet Configuration

Web.xml Configuration

<?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_5_0.xsd"
         version="5.0">
    
    <display-name>My Web Application</display-name>
    
    <!-- Servlet declarations -->
    <servlet>
        <servlet-name>UserServlet</servlet-name>
        <servlet-class>com.example.UserServlet</servlet-class>
        <init-param>
            <param-name>database.url</param-name>
            <param-value>jdbc:mysql://localhost:3306/mydb</param-value>
        </init-param>
        <init-param>
            <param-name>database.user</param-name>
            <param-value>user</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- Servlet mappings -->
    <servlet-mapping>
        <servlet-name>UserServlet</servlet-name>
        <url-pattern>/users/*</url-pattern>
    </servlet-mapping>
    
    <!-- Context parameters -->
    <context-param>
        <param-name>app.name</param-name>
        <param-value>My Application</param-value>
    </context-param>
    
    <!-- Session configuration -->
    <session-config>
        <session-timeout>30</session-timeout>
        <cookie-config>
            <name>JSESSIONID</name>
            <http-only>true</http-only>
            <secure>true</secure>
        </cookie-config>
    </session-config>
    
    <!-- Welcome files -->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
    
    <!-- Error pages -->
    <error-page>
        <error-code>404</error-code>
        <location>/WEB-INF/error/404.jsp</location>
    </error-page>
    
    <error-page>
        <error-code>500</error-code>
        <location>/WEB-INF/error/500.jsp</location>
    </error-page>
</web-app>

Annotation-based Configuration

@WebServlet(
    name = "UserServlet",
    urlPatterns = {"/users", "/users/*"},
    initParams = {
        @WebInitParam(name = "database.url", value = "jdbc:mysql://localhost:3306/mydb"),
        @WebInitParam(name = "database.user", value = "user")
    },
    loadOnStartup = 1
)
public class UserServlet extends HttpServlet {
    
    @Override
    public void init() throws ServletException {
        String dbUrl = getInitParameter("database.url");
        String dbUser = getInitParameter("database.user");
        
        // Initialize database connection
    }
}

Filters and Listeners

Servlet Filters

@WebFilter("/*")
public class LoggingFilter implements Filter {
    
    private FilterConfig filterConfig;
    
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        System.out.println("LoggingFilter initialized");
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        // Log request details
        String method = httpRequest.getMethod();
        String uri = httpRequest.getRequestURI();
        String queryString = httpRequest.getQueryString();
        String userAgent = httpRequest.getHeader("User-Agent");
        
        long startTime = System.currentTimeMillis();
        
        System.out.println("Request: " + method + " " + uri + 
                          (queryString != null ? "?" + queryString : ""));
        
        try {
            // Continue the filter chain
            chain.doFilter(request, response);
        } finally {
            // Log response details
            long duration = System.currentTimeMillis() - startTime;
            int status = httpResponse.getStatus();
            
            System.out.println("Response: " + status + " (" + duration + "ms)");
        }
    }
    
    @Override
    public void destroy() {
        System.out.println("LoggingFilter destroyed");
    }
}

@WebFilter("/secure/*")
public class AuthenticationFilter implements Filter {
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        
        HttpSession session = httpRequest.getSession(false);
        boolean isLoggedIn = (session != null && session.getAttribute("user") != null);
        
        if (!isLoggedIn) {
            // User not logged in, redirect to login page
            httpResponse.sendRedirect(httpRequest.getContextPath() + "/login");
            return;
        }
        
        // User is logged in, continue with request
        chain.doFilter(request, response);
    }
}

Event Listeners

@WebListener
public class ApplicationListener implements ServletContextListener, 
                                           HttpSessionListener {
    
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        ServletContext context = sce.getServletContext();
        
        System.out.println("Application starting up...");
        
        // Initialize application-wide resources
        initializeDatabase();
        loadConfiguration();
        
        // Set application attributes
        context.setAttribute("appStartTime", System.currentTimeMillis());
        context.setAttribute("appName", "My Web Application");
    }
    
    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("Application shutting down...");
        
        // Cleanup application resources
        closeDatabase();
        saveApplicationState();
    }
    
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("Session created: " + session.getId());
        
        // Track active sessions
        ServletContext context = session.getServletContext();
        Integer activeSessions = (Integer) context.getAttribute("activeSessions");
        if (activeSessions == null) {
            activeSessions = 0;
        }
        context.setAttribute("activeSessions", activeSessions + 1);
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        HttpSession session = se.getSession();
        System.out.println("Session destroyed: " + session.getId());
        
        // Update active session count
        ServletContext context = session.getServletContext();
        Integer activeSessions = (Integer) context.getAttribute("activeSessions");
        if (activeSessions != null && activeSessions > 0) {
            context.setAttribute("activeSessions", activeSessions - 1);
        }
    }
    
    private void initializeDatabase() {
        // Database initialization logic
    }
    
    private void loadConfiguration() {
        // Configuration loading logic
    }
    
    private void closeDatabase() {
        // Database cleanup logic
    }
    
    private void saveApplicationState() {
        // Save application state logic
    }
}

Best Practices

1. Thread Safety

public class ThreadSafeServlet extends HttpServlet {
    
    // Class-level variables are shared among all requests - avoid mutable state
    private final UserService userService = new UserService(); // OK - immutable reference
    private int counter = 0; // BAD - mutable shared state
    
    @Override
    protected void doGet(HttpServletRequest request, 
                        HttpServletResponse response) 
            throws ServletException, IOException {
        
        // Local variables are thread-safe
        String userId = request.getParameter("userId");
        User user = userService.findById(userId);
        
        // Synchronized access to shared mutable state
        synchronized (this) {
            counter++;
            System.out.println("Request count: " + counter);
        }
        
        // Better approach: use application context for shared data
        ServletContext context = getServletContext();
        synchronized (context) {
            Integer appCounter = (Integer) context.getAttribute("requestCount");
            if (appCounter == null) appCounter = 0;
            context.setAttribute("requestCount", appCounter + 1);
        }
    }
}

2. Resource Management

public class ResourceManagementServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
            throws ServletException, IOException {
        
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        
        try {
            // Get database connection
            connection = DatabaseUtil.getConnection();
            
            // Prepare statement
            statement = connection.prepareStatement("SELECT * FROM users WHERE id = ?");
            statement.setString(1, request.getParameter("userId"));
            
            // Execute query
            resultSet = statement.executeQuery();
            
            // Process results
            if (resultSet.next()) {
                // Build response
                response.setContentType("application/json");
                response.getWriter().write(buildUserJson(resultSet));
            } else {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
            }
            
        } catch (SQLException e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("{\"error\": \"Database error\"}");
        } finally {
            // Always close resources in reverse order
            closeQuietly(resultSet);
            closeQuietly(statement);
            closeQuietly(connection);
        }
    }
    
    private void closeQuietly(AutoCloseable resource) {
        if (resource != null) {
            try {
                resource.close();
            } catch (Exception e) {
                // Log error but don't throw
                System.err.println("Error closing resource: " + e.getMessage());
            }
        }
    }
    
    private String buildUserJson(ResultSet rs) throws SQLException {
        return String.format(
            "{\"id\": \"%s\", \"name\": \"%s\", \"email\": \"%s\"}",
            rs.getString("id"),
            rs.getString("name"),
            rs.getString("email")
        );
    }
}

3. Error Handling

public class ErrorHandlingServlet extends HttpServlet {
    
    @Override
    protected void doPost(HttpServletRequest request, 
                         HttpServletResponse response) 
            throws ServletException, IOException {
        
        try {
            // Validate input
            validateRequest(request);
            
            // Process request
            processUserRegistration(request, response);
            
        } catch (ValidationException e) {
            sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST, 
                            "Validation Error", e.getMessage());
        } catch (DuplicateUserException e) {
            sendErrorResponse(response, HttpServletResponse.SC_CONFLICT, 
                            "Duplicate User", e.getMessage());
        } catch (Exception e) {
            // Log unexpected errors
            System.err.println("Unexpected error in user registration: " + e.getMessage());
            e.printStackTrace();
            
            sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, 
                            "Internal Error", "An unexpected error occurred");
        }
    }
    
    private void validateRequest(HttpServletRequest request) throws ValidationException {
        String email = request.getParameter("email");
        String password = request.getParameter("password");
        
        if (email == null || email.trim().isEmpty()) {
            throw new ValidationException("Email is required");
        }
        
        if (!isValidEmail(email)) {
            throw new ValidationException("Invalid email format");
        }
        
        if (password == null || password.length() < 8) {
            throw new ValidationException("Password must be at least 8 characters");
        }
    }
    
    private void sendErrorResponse(HttpServletResponse response, int statusCode, 
                                 String error, String message) throws IOException {
        response.setStatus(statusCode);
        response.setContentType("application/json");
        response.getWriter().write(String.format(
            "{\"error\": \"%s\", \"message\": \"%s\"}", error, message));
    }
}

Summary

Java Servlets provide a powerful foundation for building web applications with several key advantages:

Core Benefits:

  • Platform independence and portability
  • Robust lifecycle management
  • Built-in session and state management
  • Integration with Java ecosystem

Key Concepts:

  • Servlet lifecycle (init, service, destroy)
  • Request/response handling patterns
  • Session management techniques
  • Filter and listener integration

Best Practices:

  • Ensure thread safety in servlet implementations
  • Proper resource management and cleanup
  • Comprehensive error handling and validation
  • Use filters for cross-cutting concerns
  • Leverage annotations for cleaner configuration

Modern Usage: While modern frameworks like Spring Boot have largely abstracted away direct servlet programming, understanding servlets remains important for:

  • Debugging web application issues
  • Understanding framework internals
  • Implementing custom filters and listeners
  • Performance optimization

Servlets continue to be the foundation that powers Java web applications, providing the essential request-response processing capabilities that higher-level frameworks build upon.