How do servlets work? Instantiation, sessions, shared variables and multithreading

instance-variables, java, multithreading, servlets, session-variables

Solution

`ServletContext`

When the servlet container (like Apache Tomcat) starts up, it will deploy and load all its web applications. When a web application is loaded, the servlet container creates an instance of `ServletContext` once and keeps it in the server's memory. The web app's `web.xml` and all of included `web-fragment.xml` files is parsed, and each `<servlet>`, `<filter>` and `<listener>` found (or each class annotated with `@WebServlet`, `@WebFilter` and `@WebListener` respectively) will be instantiated once and be kept in the server's memory as well, registred via the `ServletContext`. For each instantiated filter, its `init()` method is invoked with an instance of `FilterConfig` as argument which in turn contains the involved `ServletContext`.

When a `Servlet` has a `<servlet><load-on-startup>` or `@WebServlet(loadOnStartup)` value of `0` or greater, then its `init()` method is also invoked during startup. Those servlets are initialized in the same order specified by that value. If the same value is specified for more than one servlet, then each of those servlets is loaded in the same order as they appear in the `web.xml`, `web-fragment.xml`, or `@WebServlet` classloading. In the event the "load-on-startup" value is absent or negative, the `init()` method will be invoked whenever the HTTP request hits that servlet for the very first time. There are two `init()` methods, one taking an instance of `ServletConfig` as argument which in turn contains the involved `ServletContext`, and another which does not take any arguments but the `ServletContext` is available by inherited `getServletContext()` method.

When the servlet container is finished with all of the above described initialization steps, then the `ServletContextListener#contextInitialized()` will be invoked with a `ServletContextEvent` argument which in turn contains the involved `ServletContext`. This will allow the developer the opportunity to programmatically register yet another `Servlet`, `Filter` or `Listener`.

When the servlet container shuts down, it unloads all web applications, invokes the `destroy()` method of all its initialized servlets and filters, and all `Servlet`, `Filter` and `Listener` instances registered via the `ServletContext` are trashed. Finally the `ServletContextListener#contextDestroyed()` will be invoked and the `ServletContext` itself will be trashed.

`HttpServletRequest` and `HttpServletResponse`

The servlet container is attached to a web server that listens for HTTP requests on a certain port number (port 8080 is usually used during development and port 80 in production). When a client (e.g. user with a web browser, or programmatically using `URLConnection`) sends an HTTP request, the servlet container creates new instances of `HttpServletRequest` and `HttpServletResponse` and passes them through any defined `Filter` in the chain and, eventually, the `Servlet` instance.

In the case of filters, the `doFilter()` method is invoked. When the servlet container's code calls `chain.doFilter(request, response)`, the request and response continue on to the next filter, or hit the servlet if there are no remaining filters.

In the case of servlets, the `service()` method is invoked. By default, this method determines which one of the `doXxx()` methods to invoke based off of `request.getMethod()`. If the determined method is absent from the servlet, then an HTTP 405 error is returned in the response.

The request object provides access to all of the information about the HTTP request, such as its URL, headers, query string and body. The response object provides the ability to control and send the HTTP response the way you want by, for instance, allowing you to set the headers and the body (usually with generated HTML content from a JSP file). When the HTTP response is committed and finished, both the request and response objects are recycled and made available for reuse.

`HttpSession`

When a client visits the webapp for the first time and/or the `HttpSession` is obtained for the first time via `request.getSession()`, the servlet container creates a new `HttpSession` object, generates a long and unique ID (which you can get by `session.getId()`), and stores it in the server's memory. The servlet container also sets a `Cookie` in the `Set-Cookie` header of the HTTP response with `JSESSIONID` as its name and the unique session ID as its value.

As per the HTTP cookie specification (a contract any decent web browser and web server must adhere to), the client (the web browser) is required to send this cookie back in subsequent requests in the `Cookie` header for as long as the cookie is valid (i.e. the unique ID must refer to an unexpired session and the domain and path are correct). Using your browser's built-in HTTP traffic monitor, you can verify that the cookie is valid (press F12 in Chrome / Edge / Firefox 23+ / IE9+, and check the Net/Network tab). The servlet container will check the `Cookie` header of every incoming HTTP request for the presence of the cookie with the name `JSESSIONID` and use its value (the session ID) to get the associated `HttpSession` from server's memory.

The `HttpSession` stays alive until it has been idle (i.e. not used in a request) for more than the timeout value specified in `<session-timeout>`, a setting in `web.xml`. The default timeout value depends on the servlet container and is usually 30 minutes. So, when the client doesn't visit the web app for longer than the time specified, the servlet container trashes the session. Every subsequent request, even with the cookie specified, will not have access to the same session anymore; the servlet container will create a new session.

On the client side, the session cookie remains as long as the browser instance is running (normally). Unless the browser is configured to restore the last browser session, when the client closes the browser instance (all tabs/windows), the session is lost on the client's side. In a new browser instance, the cookie associated with the session wouldn't exist, so it would no longer be sent. This causes an entirely new `HttpSession` to be created, with an entirely new session cookie being used.

In a nutshell

- The `ServletContext` lives for as long as the web app lives. It is shared among all requests in all sessions.

- The `HttpSession` lives for as long as the client is interacting with the web app with the same browser instance, and the session hasn't timed out at the server side. It is shared among all requests in the same session.

- The `HttpServletRequest` and `HttpServletResponse` live from the time the servlet receives an HTTP request from the client, until the complete response (the web page) has arrived. It is not shared elsewhere.

- All `Servlet`, `Filter` and `Listener` instances live as long as the web app lives. They are shared among all requests in all sessions.

- Any `attribute` that is defined in `ServletContext`, `HttpServletRequest` and `HttpSession` will live as long as the object in question lives. The object itself represents the "scope" in bean management frameworks such as JSF, CDI, Spring, etc. Those frameworks store their scoped beans as an `attribute` of its closest matching scope.

Thread Safety

That said, your major concern is possibly thread safety. You should now know that servlets and filters are shared among all requests. That's the nice thing about Java, it's multithreaded and different threads (read: HTTP requests) can make use of the same instance. It would otherwise be too expensive to recreate, `init()` and `destroy()` them for every single request.

You should also realize that you should never assign any request or session scoped data as an instance variable of a servlet or filter. It will be shared among all other requests in other sessions. That's not thread-safe! The below example illustrates this:

public class ExampleServlet extends HttpServlet {

    private Object thisIsNOTThreadSafe;

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        Object thisIsThreadSafe;

        thisIsNOTThreadSafe = request.getParameter("foo"); // BAD!! Shared among all requests!
        thisIsThreadSafe = request.getParameter("foo"); // OK, this is thread safe.
    } 
}

See also:

- What is the difference between JSF, Servlet and JSP?

- Best option for Session management in Java

- Difference between / and /* in servlet mapping url pattern

- doGet and doPost in Servlets

- Servlet seems to handle multiple concurrent browser requests synchronously

- Why Servlets are not thread Safe?

- Different ways to get Servlet Context

Problem

Suppose, I have a webserver which holds numerous servlets. For information passing among those servlets I am setting session and instance variables. Now, if 2 or more users send request to this server then what happens to the session variables? Will they all be common for all the users or they will be different for each user? If they are different, then how was the server able to differentiate between different users? One more similar question, if there are `n` users accessing a particular servlet, then this servlet gets instantiated only the first time the first user accessed it or does it get instantiated for all the users separately? In other words, what happens to the instance variables?

Original source

Related problems