Recovering Session State
The servlet container maintains state using sessions. Each session holds data associated with a particular client. The container issues a unique identifier to each new session. When a request arrives, the container matches the identifier on the incoming request to a session object and attaches the session object to the request.
A session stores state as a collection of attributes, indexed by name. Each attribute is an instance of
rwsf::Attribute, a helper class that holds objects of nearly any type.
To recover state, the servlet retrieves the session from the request, gets the attribute by name, then extracts an object from the attribute. For example, the code below recovers a string value from a session:
//setting the attribute in a session
rwsf::Attribute cartAttr; //1
cartAttr << (std::string)"cart"; //2
rwsf::HttpSession session = request.getSession(true); //3
session.setAttribute("ShoppingCart", cartAttr); //4
//now recover the cart attribute
std::string cart;
rwsf::Attribute newCartAttr =
session.getAttribute("ShoppingCart"); //5
newCartAttr >> cart; //6
std::cout << cart << std::endl;
Note that if the cart attribute were to contain an object other than a string, line
//5 would assign that object to
cart. In this case, the operation would throw an
rwsf::Exception.
A
rwsf::Attribute can store an instance of any class that provides a default constructor and that is safe to assign. Note that an attribute cannot store string literals, since a string literal is an array of characters. Rather than using a string literal for an attribute, construct an
std::string, as shown below:
rwsf::Attribute stringAttr;
stringAttr << std::string("A string");
Finally, notice that if a client sends multiple or overlapping requests, more than one thread may have access to the same session at the same time. The servlet is responsible for managing concurrency issues on attributes stored within sessions.
The getSession() function always returns a session. If the client request contained a session identifier, the function returns the session matching that identifier. Otherwise, the function returns a new session. To determine if the session is new, use the isNew() function. For example, the code sample below adds an attribute to a new session. For an existing session, the servlet retrieves an attribute and tests to see if the attribute is valid. If so, the servlet extracts the contents of the attribute. If not, the servlet resets the attribute:
rwsf::HttpSession session = request.getSession();
std::string username = "unknown";
if (session.isNew())
{
rwsf::Attribute attr;
attr << username;
session.setAttribute("username", attr);
}
else
{
rwsf::Attribute attr = session.getAttribute("username");
if (attr.isValid())
{
try {
attr >> username;
}
catch (const rwsf::Exception& e) {
log ("Error converting username attribute to std::string. " +
e.what());
attr << username;
session.setAttribute("username", attr);
}
}
else
{
attr << username;
session.setAttribute("username", attr);
}
}
For new sessions, the servlet container uses cookies to store the session identifier on the client. However, some clients either do not accept or do not support cookies. The servlet container can also recover session information stored in the URL transmitted by the client. Each session provides a pair of functions, setFromCookie() and setFromURL(), that specify how a new session should transmit the session identifier to the client. HydraExpress uses cookies whenever possible, so the container ignores the setFromURL() function for an existing session recovered from a cookie. The following line of code sets a new session to use URL rewriting:
session.setFromURL();
Note that if a session uses URL rewriting, any URL transmitted directly to the client must contain the session identifier. Use the
encodeURL() function of
rwsf::HttpServletResponse to add the session identifier to client URLs. For example, the code below creates an HTML form tag that includes session information in the target URL:
std::string myURL = response.encodeURL(request.getContextPath() +
request.getServletPath());
std::string formOpenTag = "<form method=\"post\" action=\"" +
myURL + "\">";
The request holds the information on whether the client returned the session identifier as a cookie, or whether the client returned the session identifier in the URL. Function isRequestedSessionIdFromURL() returns true if the client returned the session ID in the URL, while function isRequestedSessionIdFromCookie() returns true if the client returned the session ID in a cookie. Notice that if the client did not return a session identifier, both functions return false.
The servlet container destroys sessions that have not been active for a given period of time. New sessions are given the timeout specified in the context
web.xml file (see
Configure Sessions), or 30 seconds if that file does not specify a timeout. The function
setMaxInactiveInterval() sets the amount of time the session may remain inactive before the container destroys the session. The code below allows a session to remain inactive for up to 10 minutes:
session.setMaxInactiveInterval(600);
If no client accesses session for a 10 minute period (600 seconds), the container destroys the session.