How do I use ExecutorService to run tasks in Java?

In Java, the ExecutorService interface is part of the java.util.concurrent package and provides a higher-level replacement for managing threads and tasks. It simplifies the execution of tasks in a multithreaded environment by abstracting thread creation and management.

Here’s how you can use ExecutorService to run tasks in Java:


1. Creating an ExecutorService

You can create an instance of ExecutorService using the factory methods provided by the Executors class. Some common options are:

  • Single-threaded pool:
    ExecutorService executor = Executors.newSingleThreadExecutor();
    
  • Fixed-size thread pool:
    ExecutorService executor = Executors.newFixedThreadPool(4); // 4 threads in the pool
    
  • Cached thread pool (dynamic sizing):
    ExecutorService executor = Executors.newCachedThreadPool();
    
  • Scheduled thread pool (for tasks that need scheduling or delayed execution):
    ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
    

2. Submitting Tasks

You can submit tasks (runnable or callable) to the ExecutorService for execution:

  • Using Runnable:
    The Runnable interface doesn’t return a result or throw checked exceptions.

    executor.submit(() -> {
      System.out.println("Running a task in thread: " + Thread.currentThread().getName());
    });
    
  • Using Callable:
    The Callable interface allows the task to return a result and throw exceptions.

    Future<Integer> future = executor.submit(() -> {
      System.out.println("Calculating result in " + Thread.currentThread().getName());
      return 42; // returning a result
    });
    
    // Retrieve the result
    try {
      Integer result = future.get();
      System.out.println("Result: " + result);
    } catch (Exception e) {
      e.printStackTrace();
    }
    

3. Shutting Down the ExecutorService

You need to shut down the ExecutorService once you’ve completed submitting tasks:

  • Graceful shutdown:
    This stops accepting new tasks and allows the currently running tasks to complete.

    executor.shutdown();
    try {
      if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
          executor.shutdownNow(); // Force shutdown if timeout happens
      }
    } catch (InterruptedException e) {
      executor.shutdownNow();
    }
    
  • Forceful shutdown:
    This halts all running tasks and stops new ones immediately.

    executor.shutdownNow();
    

4. Example: Submitting Multiple Tasks

package org.kodejava.util.concurrent;

import java.util.concurrent.*;

public class ExecutorServiceExample {
    public static void main(String[] args) {
        // Create a fixed thread pool with 3 threads
        ExecutorService executor = Executors.newFixedThreadPool(3);

        // Submit Runnable tasks
        for (int i = 0; i < 5; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " is running by " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000); // Simulate work
                } catch (InterruptedException e) {
                    System.err.println("Task " + taskId + " was interrupted!");
                }
            });
        }

        // Shutdown the executor gracefully
        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow(); // Force shutdown if tasks exceed timeout
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }

        System.out.println("All tasks finished.");
    }
}

5. Choosing Between Runnable and Callable

  • Use Runnable when your task does not need to return a result.
  • Use Callable when your task needs to return a result or throw checked exceptions.

Advanced Features

If you need to manage periodic tasks or delayed execution, use ScheduledExecutorService:

ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);

// Schedule a task to run after a delay
scheduler.schedule(() -> System.out.println("Task executed after delay"), 3, TimeUnit.SECONDS);

// Schedule a task to run repeatedly at fixed intervals
scheduler.scheduleAtFixedRate(() -> System.out.println("Recurring task"), 1, 5, TimeUnit.SECONDS);

Summary

  1. Create an ExecutorService instance (e.g., fixed thread pool, cached thread pool).
  2. Submit tasks (Runnable or Callable) using submit().
  3. Shut down the executor service gracefully (shutdown() and awaitTermination()).
  4. Use Callable and Future for tasks that need to return results.

This abstraction helps manage your threads efficiently and avoids the complexities of low-level thread creation and management.

How do I manage sessions using HttpSession in Jakarta Servlets?

Managing sessions using HttpSession in Jakarta Servlets is a straightforward process. HttpSession is a part of the Jakarta Servlet API which provides a way to handle session management between a client and server during multiple requests.

Here’s a structured guide on using and managing sessions with HttpSession:


1. Introduction to HttpSession

  • The HttpSession interface is used to:
    • Store information about a user’s session (attributes such as user information, preferences, or data specific to each client).
    • Track users across multiple requests (via cookies or URL rewriting).

2. How to Create or Retrieve a Session

You can retrieve or create a session using the HttpServletRequest.getSession() method:

HttpSession session = request.getSession();
  • If a session already exists, this method returns the existing session.
  • If no session exists, it will create a new one.

If you want to retrieve the session but don’t want to create a new one if it does not exist, you can use:

HttpSession session = request.getSession(false); // Returns null if no session exists

3. Adding Attributes to the Session

You can store user-related or application-specific data in the session using the setAttribute method:

session.setAttribute("user", "JohnDoe");
session.setAttribute("cartItems", cartItemList);

4. Retrieving Attributes from the Session

Use the getAttribute method to retrieve values stored in the session:

String user = (String) session.getAttribute("user");
List<String> cartItems = (List<String>) session.getAttribute("cartItems");

Make sure to cast the returned object to the proper type.


5. Removing Attributes from the Session

Use the removeAttribute method to delete specific session attributes:

session.removeAttribute("user");

6. Invalidating or Destroying the Session

When the session is no longer needed (e.g., a user logs out), you can invalidate the session using:

session.invalidate();

This method:

  1. Invalidates the current session and removes all the stored attributes.
  2. Creates a new session on the next request.getSession() call.

7. Setting Session Timeout

You can specify the session timeout (in minutes) using:

session.setMaxInactiveInterval(30 * 60); // 30 minutes

To retrieve the current session timeout:

int timeout = session.getMaxInactiveInterval();

If the user is inactive for longer than the timeout duration, the session will be invalidated automatically.


8. Session ID

Each HttpSession has a unique session ID. You can retrieve it using:

String sessionId = session.getId();

This ID is used to track the session between requests (usually via cookies or URL rewriting).


9. Checking Session Validity

You can verify whether a session is new using:

boolean isNew = session.isNew();

This is particularly useful when you want to check if the session was newly created or reused.


10. Session Tracking Mechanisms

The server manages session tracking using one of the following mechanisms:

  1. Cookies: This is the default method where the session ID is maintained using a cookie (e.g., JSESSIONID).
  2. URL Rewriting: This is a fallback mechanism when cookies are disabled. The session ID is appended to the URL as a query parameter.

For enabling URL rewriting, you can use:

String encodedURL = response.encodeURL("yourUrlHere");

This ensures the session ID is included in the URL if cookies are not supported.


11. Example Code: Using HttpSession

Here’s a full working example:

package org.kodejava.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 jakarta.servlet.http.HttpSession;
import java.io.IOException;

@WebServlet("/sessionExample")
public class SessionExampleServlet extends HttpServlet {

  @Override
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    // Retrieve session, create if none exists
    HttpSession session = request.getSession();

    // Set session attributes
    session.setAttribute("username", "JohnDoe");

    // Get session attributes
    String username = (String) session.getAttribute("username");

    // Display session info
    response.setContentType("text/html");
    response.getWriter().println("<h1>Welcome, " + username + "</h1>");

    // Show session ID
    response.getWriter().println("<p>Session ID: " + session.getId() + "</p>");
  }

  @Override
  protected void doPost(HttpServletRequest request, HttpServletResponse response)
          throws ServletException, IOException {
    // Invalidate session on logout
    HttpSession session = request.getSession(false);
    if (session != null) {
      session.invalidate();
    }
    response.getWriter().write("Logged out successfully!");
  }
}

12. Best Practices for HttpSession

  • Minimize Data Storage: Store only necessary, lightweight data in the session to reduce memory overhead.
  • Secure Session Handling:
    • Ensure cookies are marked as HttpOnly and Secure.
    • Implement proper session timeout.
    • Use HTTPS to protect the session ID during transmission.
  • Invalidate Sessions on Logout: Always invalidate the session to clear sensitive data when users log out.

By following these steps, you can effectively manage user sessions using HttpSession in your Jakarta Servlets-based application.

Maven dependencies

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

Maven Central

How do I read request parameters using HttpServletRequest?

In a Jakarta EE (or formerly Java EE) web application, you can use the HttpServletRequest object to read request parameters sent by a client (e.g., from a form, URL query string, or other mechanisms). Below are the common ways to read the parameters:

1. Using getParameter(String name)

This method is used when you need to retrieve a single request parameter by its name. If the parameter doesn’t exist, it will return null.

package org.kodejava.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("/myServlet")
public class ExampleServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      // Retrieve a single parameter
      String paramValue = request.getParameter("paramName");

      // Check if the parameter exists
      if (paramValue != null) {
         response.getWriter().println("Value of paramName: " + paramValue);
      } else {
         response.getWriter().println("Parameter 'paramName' not found.");
      }
   }
}

2. Using getParameterNames()

This method allows you to retrieve all parameter names as an Enumeration<String>. You can then iterate through them to get individual values.

package org.kodejava.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;
import java.util.Enumeration;

@WebServlet("/myServlet")
public class ExampleServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      Enumeration<String> parameterNames = request.getParameterNames();

      while (parameterNames.hasMoreElements()) {
         String paramName = parameterNames.nextElement();
         String paramValue = request.getParameter(paramName);

         response.getWriter().println(paramName + ": " + paramValue);
      }
   }
}

3. Using getParameterValues(String name)

Use this method if you expect the parameter to have multiple values (e.g., a checkbox group or multiple selections from a dropdown), as it returns an array of String.

package org.kodejava.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("/myServlet")
public class ExampleServlet extends HttpServlet {

   @Override
   protected void doPost(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      // Retrieve multiple values for a single parameter
      String[] values = request.getParameterValues("multiParamName");

      if (values != null) {
         response.getWriter().println("Values of multiParamName:");
         for (String value : values) {
            response.getWriter().println(value);
         }
      } else {
         response.getWriter().println("No values provided for 'multiParamName'.");
      }
   }
}

4. Using getParameterMap()

If you need to retrieve all parameters along with their values, you can use getParameterMap(). This returns a Map<String, String[]> where the key is the parameter name, and the value is an array of String containing parameter values.

package org.kodejava.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;
import java.util.Map;

@WebServlet("/myServlet")
public class ExampleServlet extends HttpServlet {

   @Override
   protected void doGet(HttpServletRequest request, HttpServletResponse response)
           throws ServletException, IOException {
      Map<String, String[]> parameterMap = request.getParameterMap();

      for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
         String paramName = entry.getKey();
         String[] paramValues = entry.getValue();

         response.getWriter().println(paramName + ": ");
         for (String value : paramValues) {
            response.getWriter().println("\t" + value);
         }
      }
   }
}

Example Usage Scenarios:

  1. GET Request with Query Parameters:
    URL: `http://localhost:8080/myServlet?param1=value1&param2=value2`

    • request.getParameter("param1") will return "value1"
    • request.getParameter("param2") will return "value2"
  2. POST Request with Form Data:
    If a form submits data like:

    <form method="post" action="/myServlet">
       <input type="text" name="username" value="john123" />
       <input type="password" name="password" value="secret" />
       <input type="submit" />
    </form>
    
    • Use request.getParameter("username") to get "john123".
    • Use request.getParameter("password") to get "secret".

Make sure to handle null values and encode your response properly when writing them back to the client to prevent issues like XSS (Cross-Site Scripting).

Maven dependencies

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

Maven Central

How do I handle GET and POST requests in Jakarta Servlets?

Handling GET and POST requests in Jakarta Servlets involves overriding the doGet() and doPost() methods provided by the HttpServlet class. Here’s a step-by-step guide:


1. Import Necessary Packages

First, ensure you import the jakarta.servlet and jakarta.servlet.http packages in your servlet class.

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;

2. Create a Servlet Class

Your servlet class should extend the HttpServlet class. The doGet() method will handle GET requests, while the doPost() method will handle POST requests.


3. Override doGet and doPost Methods

  • Use the HttpServletRequest object to get request data.
  • Use the HttpServletResponse object to send a response to the client.

4. Example Code

Here’s a complete example:

package org.kodejava.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;
import java.io.PrintWriter;

@WebServlet("/example")  // Annotation to map this servlet to "/example"
public class ExampleServlet extends HttpServlet {

    // Handles GET requests
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // Set response content type
        response.setContentType("text/html");

        // Get query parameter (e.g., ?name=John)
        String name = request.getParameter("name");

        // Prepare response
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>Hello, " + (name != null ? name : "Guest") + "!</h1>");
        out.println("<form method='POST' action='/example'>");
        out.println("<label for='postName'>Enter your name:</label>");
        out.println("<input type='text' id='postName' name='name'>");
        out.println("<button type='submit'>Submit</button>");
        out.println("</form>");
        out.println("</body></html>");
    }

    // Handles POST requests
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        // Set response content type
        response.setContentType("text/html");

        // Get form data (e.g., from POST body)
        String name = request.getParameter("name");

        // Prepare response
        PrintWriter out = response.getWriter();
        out.println("<html><body>");
        out.println("<h1>Thank you, " + (name != null ? name : "Guest") + "!</h1>");
        out.println("<a href='/example'>Go back</a>");
        out.println("</body></html>");
    }
}

5. Explanation

  • Annotations: The @WebServlet("/example") annotation maps this servlet to the /example URL.
  • GET and POST
    • The doGet() method handles requests sent as GET, typically used when the client fetches data.
    • The doPost() method handles requests sent as POST, commonly used to send form data to the server.
  • Parameters: Use request.getParameter("paramName") to retrieve parameters from the request.

6. Deploy and Test

  1. Add the servlet to your Jakarta EE web application.
  2. Access the servlet:
    • GET request: Open `http://localhost:8080/your-app-context/example` in a browser.
    • POST request: Submit the HTML form created in doGet() or use a tool like Postman.

7. Keynotes

  • Always handle exceptions (IOException and ServletException) properly in production.
  • Use appropriate HTTP response headers and status codes.
  • Consider character encoding (request.setCharacterEncoding("UTF-8") and response.setCharacterEncoding("UTF-8")) for supporting special characters.

This is how you handle GET and POST requests in a Jakarta Servlet effectively!

Maven dependencies

<dependency>
    <groupId>jakarta.servlet</groupId>
    <artifactId>jakarta.servlet-api</artifactId>
    <version>6.1.0</version>
    <scope>provided</scope>
</dependency>

Maven Central

How do I deploy a WAR file in Tomcat Servlet Container?

In this post I will show you the step-by-step instructions to deploy a WAR file in Tomcat 11:

1. Build Your WAR File

A WAR file (.war) is a packaged web application. To build it:

  • If you’re using Maven, run the following command in your project directory:
    mvn clean package
    

    This generates a WAR file under the target/ folder.

  • If you’re using other tools (e.g., Gradle, IDE), export the WAR file using the packaging/export option.

2. Install and Configure Tomcat 11

  • Download Tomcat 11 from the official website: Apache Tomcat Downloads
  • Extract the downloaded ZIP/TAR file to a desired directory.
  • Navigate to the conf/ directory and optionally configure the file server.xml to customize ports, define your hostname, or adjust server parameters (optional for default usage).

3. Start Tomcat

  • Navigate to the bin/ directory.
  • Run the startup script as per your operating system:

    • On Windows:
      startup.bat
      
    • On Linux/Mac:
      ./startup.sh
      
  • By default, Tomcat runs on port 8080. Check if it is running by accessing:
    http://localhost:8080/
    

    If Tomcat is running, its welcome page should appear.

4. Deploy Your WAR File

You have two primary ways to deploy the WAR file:

a) Manual Deployment

  1. Copy the WAR file to Tomcat’s webapps/ directory:
    • Navigate to the webapps/ directory inside your Tomcat installation path.
    • Copy your WAR file (e.g., HelloWorld.war) into this folder.
      cp /path/to/HelloWorld.war /path/to/tomcat/webapps/
      
  2. Restart Tomcat:
    • Stop and restart Tomcat for new deployments to take effect, using:
      shutdown.bat/startup.bat (Windows)
      ./shutdown.sh && ./startup.sh (Linux/Mac)
      
  3. Access Your Application:
    • By default, the application will be available at:
      http://localhost:8080/<war-file-name>/
      
    • For example:
      http://localhost:8080/HelloWorld/
      

b) Upload via Tomcat Manager (Web UI)

  1. Go to the Tomcat Manager application at:
  2. Log in with your username and password:
    • Default username: admin
    • Default password: admin (or use the one you set in tomcat-users.xml file under conf/).
      To set new credentials, update the conf/tomcat-users.xml file by adding:

      <role rolename="manager-gui"/>
      <user username="admin" password="admin" roles="manager-gui"/>
      
  3. Scroll to the Deploy section:
    • Under “WAR file to deploy,” choose the WAR file from your system.
    • Click the Deploy button.
  4. Access Your Application:
    • The application URL will be:
      http://localhost:8080/<war-file-name>/
      

5. Check Logs for Errors (if any)

If the application is not working, check Tomcat logs:

  • Log files are located in the logs/ directory of your Tomcat installation.
  • The most relevant logs include:
    • catalina.<date>.log – Main log file for Tomcat.
    • localhost.<date>.log – Logs specific to deployed applications.

6. Stop Tomcat (if needed)

If you wish to shut down Tomcat:

  • Navigate to the bin/ directory.
  • Use the shutdown script:
    • On Windows:
      shutdown.bat
      
    • On Linux/Mac:
      ./shutdown.sh
      

Summary of Key Directories in Tomcat

Directory Purpose
bin/ Contains scripts to start and stop Tomcat.
conf/ Stores configuration files like server.xml.
webapps/ Holds deployed WAR files.
logs/ Contains log files for troubleshooting.