컨트롤러단에서 HttpServletRequest 파라미터로 선언 후 getSession(true) 메소드를 호출하면 세션이 존재하지 않을 경우 새로운 세션을 생성하는데 그 과정을 훑어보자. 우선 ReqyestFacade(org.apache.catalina.connector) -> Request(org.apache.catalina.connector)의 getSession 메소드가 호출되고 결론적으로 doGetSession 메소드 내에서 세션을 생성 또는 존재할 경우 반환하는데 doGetSession 메소드를 보면 요청 시 세션ID를 쿠키에 담아 보낸 경우 해당 세션이 톰캣 세션저장소에 저장된 세션과 일치할 경우 그 즉시 세션을 반환하고 아닐 경우 다음으로 진행된다.
ManagerBase 클래스의 createSession 메소드를 통해 비어있는 StandardSession(org.apache.catalina.session) 클래스를 생성하고 메소드를 호출해 각종 필드값을 설정한다. 그 중 generateSessionId 메소드를 통해 랜덤한 32자리 값을 생성하여 id 필드에 대입한다.
그 다음 ApplicationSessionCookieConfig.createSessionCookie 메소드를 호출 해 Cookie(jakarta.servlet.http) 클래스를 생성하는데 name은 JSESSIONID, value는 생성한 세션id 가 설정되고 HttpOnly = true, Path = /, Max-Age = -1, Partitioned = false 속성이 설정하여 쿠키가 생성된다.
response.addSessionCookieInternal 메소드를 호출해 생성한 쿠키 클래스를 바탕으로 response 헤더를 생성하여 설정하는데 Response(org.apache.catalina.connector) -> Response(org.apache.coyote) 의 addHeader 를 호출하여 헤더 이름은 Set-Cookie, 값은 JSESSIONID=7D825263CFB00C5D6012E4330B589564; Path=/; HttpOnly 같은 형태로 RFC6265 규칙에 맞게 헤더를 설정한다.
위에 쿠키클래스에 Max-Age = -1 값을 저장했지만 0보다 작을 경우 Max-Age 헤더를 생략한다.
이렇게 생성한 세션은 아파치 톰캣의 세션 저장소에 저장 된다. 톰캣의 세션 저장소는 톰캣이 점유중인 메모리 공간 내부에 있고, 그 톰캣은 결국 물리적인 서버 메모리에서 동작되므로 결국 서버 메모리에 저장된다고 보는게 맞을 것이다.
package org.apache.catalina.connector;
public class Request implements HttpServletRequest {
protected Session doGetSession(boolean create) {
...
...
if ((session != null) && !session.isValid()) {
session = null;
}
if (session != null) {
return session;
}
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;
}
}
String sessionId = getRequestedSessionId();
...
...
session = manager.createSession(sessionId);
// Creating a new session cookie based on that session
if (session != null && trackModesIncludesCookie) {
Cookie cookie =
ApplicationSessionCookieConfig.createSessionCookie(context, session.getIdInternal(), isSecure());
response.addSessionCookieInternal(cookie);
}
if (session == null) {
return null;
}
session.access();
return session;
}
}
public abstract class ManagerBase extends LifecycleMBeanBase implements Manager {
public Session createSession(String sessionId) {
...
...
Session session = createEmptySession();
// Initialize the properties of the new session and return it
session.setNew(true);
session.setValid(true);
session.setCreationTime(System.currentTimeMillis());
session.setMaxInactiveInterval(getContext().getSessionTimeout() * 60);
String id = sessionId;
if (id == null) {
id = generateSessionId();
}
session.setId(id);
sessionCounter++;
...
...
return session;
}
}
package org.apache.coyote;
public final class Response {
...
...
final MimeHeaders headers = new MimeHeaders();
public void addHeader(String name, String value, Charset charset) {
char cc = name.charAt(0);
if (cc == 'C' || cc == 'c') {
if (checkSpecialHeader(name, value)) {
return;
}
}
MessageBytes mb = headers.addValue(name);
if (charset != null) {
mb.setCharset(charset);
}
mb.setString(value);
}
}