Note of Java Servlet session
Tags: java
Published: 2024-01-31 18:57:54

## Notice

I wrote this article and was originally published on [Qiita](https://qiita.com/saladlam/items/6f88c122486ac539282e) on 18 March 2023.

---

# API

A Java Servlet (Servlet) application uses API methods below to access session related function.

```java
package javax.servlet.http;

public interface HttpServletRequest extends ServletRequest {
    public HttpSession getSession(boolean create);
    public HttpSession getSession();
    public String changeSessionId();
    public String getRequestedSessionId();
    public boolean isRequestedSessionIdValid();
    public boolean isRequestedSessionIdFromCookie();
    public boolean isRequestedSessionIdFromURL();
    @Deprecated
    public boolean isRequestedSessionIdFromUrl();
    ...
}
```

- These methods are implemented by Servlet container (e.g. Tomcat)
- Session object has interface *javax.servlet.http.HttpSession*, and normally its lifecycle is maintainanced by Servlet container. In Tomcat it is *org.apache.catalina.session.StandardSession*, wrapped by *org.apache.catalina.session.StandardSessionFacade*

# Association between HTTP connection and session object

HTTP is stateless protocol, when Servlet application needs to store something into session object, it calls following methods to retrieve it.

```java
javax.servlet.http.HttpServletRequest.getSession();
// or
javax.servlet.http.HttpServletRequest.getSession(true);
```

Servlet container instantiates session object, creates session id and associates it with HTTP connection by writing session cookie information (default identifier is JSESSIONID).

```text
Set-Cookie: JSESSIONID=14AE061861E1F5979F28A3D5D742A850; Path=/; HttpOnly
```

The implementation of Tomcat 9 is *org.apache.catalina.connector.Request#doGetSession*. Writing 
session cookie is performed by following code

```java
        // Creating a new session cookie based on that session
        if (session != null && trackModesIncludesCookie) {
            Cookie cookie = ApplicationSessionCookieConfig.createSessionCookie(
                    context, session.getIdInternal(), isSecure());

            response.addSessionCookieInternal(cookie);
        }
```

*org.apache.catalina.connector.Response#addSessionCookieInternal* is called

```java
    /**
     * Special method for adding a session cookie as we should be overriding
     * any previous.
     *
     * @param cookie The new session cookie to add the response
     */
    public void addSessionCookieInternal(final Cookie cookie) {
        if (isCommitted()) {
            return;
        }

        String name = cookie.getName();
        final String headername = "Set-Cookie";
        final String startsWith = name + "=";
        String header = generateCookieString(cookie);
        boolean set = false;
        MimeHeaders headers = getCoyoteResponse().getMimeHeaders();
        int n = headers.size();
        for (int i = 0; i < n; i++) {
            if (headers.getName(i).toString().equals(headername)) {
                if (headers.getValue(i).toString().startsWith(startsWith)) {
                    headers.getValue(i).setString(header);
                    set = true;
                }
            }
        }
        if (!set) {
            addHeader(headername, header);
        }
    }
```

In next HTTP request, the session id is provided in cookie header

```text
GET /login HTTP/1.1
Host: localhost:8080
User-Agent: Mozilla/5.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: http://localhost:8080/
Connection: keep-alive
Cookie: JSESSIONID=14AE061861E1F5979F28A3D5D742A850
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: same-origin
```

The application gets saved data by retrieving session object, calls

```java
javax.servlet.http.HttpServletRequest.getSession();
// or
javax.servlet.http.HttpServletRequest.getSession(false);
``` 

In Tomcat 9 retrieves session object is also processed by *org.apache.catalina.connector.Response#addSessionCookieInternal*. Following code is for loading session object

```java
        // Return the requested session if it exists and is valid
        Manager manager = context.getManager();
        if (manager == null) {
            return null;      // Sessions are not supported
        }
        if (requestedSessionId != null) {
            try {
                session = manager.findSession(requestedSessionId);
            } catch (IOException e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("request.session.failed", requestedSessionId, e.getMessage()), e);
                } else {
                    log.info(sm.getString("request.session.failed", requestedSessionId, e.getMessage()));
                }
                session = null;
            }
            if ((session != null) && !session.isValid()) {
                session = null;
            }
            if (session != null) {
                session.access();
                return session;
            }
        }
```

# Other API calls

```java
// change session id, but session object created in Servlet container will not be destroyed
javax.servlet.http.HttpServletRequest.changeSessionId();

// Unassociate current session object
javax.servlet.http.HttpSession.invalidate();
```