We don’t agree that anybody with half a brain disables cookies. In fact, most browsers do have cookies enabled, and everything’s wonderful. But there’s no guarantee.
If your app depends on sessions, you need a different way for the client and Container to exchange session ID info. Lucky for you, the Container can handle a cookie-refusing client, but it takes a little more effort from you.
If you use the session code on the previous pages—calling getSession() on the request—the Container tries to use cookies. If cookies aren’t enabled, it means the client will never join the session. In other words, the session’s isNew() method will always return true.
A client with cookies disabled will ignore “Set-Cookie” response headers
If a client doesn’t accept cookies, you won’t get an exception. No bells and sirens going off to tell you that your attempt to have a session with this client went wrong. No, it just means the client ignores your attempt to set a cookie with the session ID. In your code, if you do NOT use URL rewriting, it means that getSession() will always return a NEW session (i.e. one that always returns “true” when you call isNew() on it). The client simply never sends back a request that has a session ID cookie header.
If the client won’t take cookies, you can use URL rewriting as a backup. Assuming you do your part correctly, URL rewriting will always work—the client won’t care that it’s happening and won’t do anything to prevent it. Remember the goal is for the client and Container to exchange session ID info. Passing cookies back and forth is the simplest way to exchange session IDs, but if you can’t put the ID in a cookie, where can you put it? URL rewriting takes the session ID that’s in the cookie and sticks it right onto the end of every URL that comes in to this app.
Imagine a web page where every link has a little bit of extra info (the session ID) tacked onto the end of the URL. When the user clicks that “enhanced” link, the request goes to the Container with that extra bit on the end, and the Container simply strips off the extra part of the request URL and uses it to find the matching session.
If cookies don’t work, the Container falls back to URL rewriting, but only if you’ve done the extra work of encoding all the URLs you send in the response. If you want the Container to always default to using cookies first, with URL rewriting only as a last resort, you can relax. That’s exactly how it works (except for the first time, but we’ll get to that in a moment). But if you don’t explicitly encode your URLs, and the client won’t accept cookies, you don’t get to use sessions. If you do encode your URLs, the Container will first attempt to use cookies for session management, and fall back to URL rewriting only if the cookie approach fails.
You might have a scenario in which you want to redirect the request to a different URL, but you still want to use a session. There’s a special URL encoding method just for that:
response.encodeRedirectURL("/BeerTest.do")
URL rewriting is automatic... but only if you encode your URLs. YOU have to run all your URLs through a method of the response object—encodeURL() or encodeRedirectURL()—and the Container does everything else.
URL encoding is handled by the Response!
Don’t forget that the encodeURL() method is something you call on your HttpServletResponse object! You don’t call it on the request, or on your context, or your session object. Just remind yourself that URL encoding is all about the response.
Don’t be fooled by a request parameter “jsessionid” or a “JSESSIONID” header.
YOU don’t ever use “jsessionid” yourself. If you see a “jsessionid” request parameter, somebody’s doing something wrong. You should never see something like this:
And you shouldn’t see a custom “jsessionid” header in a request or response:
In fact, the ONLY place a “jsessionid” belongs is inside a cookie header:
POST /select/selectBeerTaste.do HTTP/1.1 User-Agent: Mozilla/5.0 Cookie: JSESSIONID=0AAB6C8DE415
This is right, but you don’t do it yourself.
or appended to the end of a URL as “extra info”:
POST /select/selectBeerTaste.do;jsessionid=0AAB6C8DE415
The result of URL rewriting (you don’t do this yourself either).
The client comes in, starts a session, then changes her mind and leaves the site. Or the client comes in, starts a session, then her browser crashes. Or the client comes in, starts a session, and then completes the session by making a purchase (shopping cart check-out). Or her computer crashes. Whatever.
The point is, session objects take resources. You don’t want sessions to stick around longer than necessary. Remember, the HTTP protocol doesn’t have any mechanism for the server to know that the client is gone. (In distributed application terms, for those of you familiar with them— there’s no leasing.)[6]
But how does the Container (or you) know when the client walked away? How does the Container know when the client’s browser crashed? How does the Container know when it’s safe to destroy a session?
What are strategies you (and the Container) might use to manage the number of sessions, and eliminate unneeded sessions? What are some possible ways in which the Container could tell that a session is no longer needed?
Think about it, then look at the HttpSession API a few pages from now for clues.
We’d like the Container to recognize when a session has been inactive for too long, and destroy the session. Of course we might have to fight the Container over what “too long” really means. Is 20 minutes too long? An hour? A day? (Maybe there’s a way for us to tell the Container what “too long” is.)
All you care about when you call getSession() is that you get an instance of a class that implements the HttpSession interface. It’s the Container’s job to create the implementation.
Once you have a session, what can you do with it?
Most of the time, you’ll use sessions to get and set session-scoped attributes.
But there’s more, of course. See if you can figure out some of the key methods for yourself (answers are on the next page, so don’t turn the page!)
<<interface>> javax.servlet.http.HttpSession |
Object getAttribute(String) long getCreationTime() String getId() long getLastAccessedTime() int getMaxInactiveInterval() ServletContext getServletContext() void invalidate() boolean isNew() void removeAttribute(String) void setAttribute(String, Object) void setMaxInactiveInterval(int) // a few more methods |
You already know about the methods for attributes (getAttribute(), setAttribute(), removeAttribute()), but here are a few key ones you might need in your application (and that might be on the exam).
What it does | What you’d use it for | |
---|---|---|
getCreationTime() | Returns the time the session was first created. | To find out how old the session is. You might want to restrict certain sessions to a fixed length of time. For example, you might say, “Once you’ve logged in, you have exactly 10 minutes to complete this form...” |
getLastAccessedTime() | Returns the last time the Container got a request with this session ID (in milliseconds). | To find out when a client last accessed this session. You might use it to decide that if the client’s been gone a long time you’ll send them an email asking if they’re coming back. Or maybe you’ll invalidate() the session. |
setMaxInactiveInterval() | Specifies the maximum time, in seconds, that you want to allow between client requests for this session. | To cause a session to be destroyed after a certain amount of time has passed without the client making any requests for this session. This is one way to reduce the amount of stale sessions sitting in your server. |
getMaxInactiveInterval() | Returns the maximum time, in seconds, that is allowed between client requests for this session. | To find out how long this session can be inactive and still be alive. You could use this to judge how much more time an inactive client has before the session will be invalidated. |
invalidate() | Ends the session. This includes unbinding all session attributes currently stored in this session. (More on that later in this chapter.) | To kill a session if the client has been inactive or if you KNOW the session is over (for example, after the client does a shopping check-out or logs out). The session instance itself might be recycled by the Container, but we don’t care. Invalidate means the session ID no longer exists, and the attributes are removed from the session object. |
Good news: you don’t have to keep track of this yourself. See those methods on the opposite page? You don’t have to use them to get rid of stale (inactive) sessions. The Container can do it for you.
Three ways a session can die:
It times out
You call invalidate() on the session object
The application goes down (crashes or is undeployed)
Configuring session timeout in the DD
Configuring a timeout in the DD has virtually the same effect as calling setMaxInactiveInterval() on every session that’s created.
Setting session timeout for a specific session
If you want to change the session-timeout value for a particular session instance (without affecting the timeout length for any other sessions in the app):
Timeouts in the DD are in MINUTES!
Here’s a big inconsistency to watch out for... you specify timeouts in the DD using MINUTES, but if you set a timeout programmatically, you specify SECONDS!
*The session, not the client.
Although cookies were originally designed to help support session state, you can use custom cookies for other things. Remember, a cookie is nothing more than a little piece of data (a name/value String pair) exchanged between the client and server. The server sends the cookie to the client, and the client returns the cookie when the client makes another request.
One cool thing about cookies is that the user doesn’t have to get involved—the cookie exchange is automatic (assuming cookies are enabled on the client, of course).
By default, a cookie lives only as long as a session; once the client quits his browser, the cookie disappears. That’s how the “JSESSIONID” cookie works. But you can tell a cookie to stay alive even AFTER the browser shuts down.
That way, your web app can still get the cookie information even though the session with that client is long gone. Imagine that Kim wants to display the user’s name each time he returns to the beer site. So he sets the cookie the first time he receives the client’s name, and if he gets the cookie back with a request, he knows not to ask for the name again. And it doesn’t matter if the user restarted his browser and hasn’t been on the site for a week!
You can use cookies to exchange name/value String pairs between the server and the client.
The server sends the cookie to the client, and the client sends it back with each subsequent request.
Session cookies vanish when the client’s browser quits, but you CAN tell a cookie to persist on the client even after the browser shuts down.
You can get cookie-related headers out of the HTTP request and response, but don’t. Everything you need to do with cookies has been encapsulated in the Servlet API in three classes: HttpServletRequest, HttpServletResponse, and Cookie.
Creating a new Cookie
Setting how long a cookie will live on the client
Sending the cookie to the client
response.addCookie(cookie);
Getting the cookie(s) from the client request
Cookie[] cookies = request.getCookies(); for (int i = 0; i < cookies.length; i++) { Cookie cookie = cookies[i]; if (cookie.getName().equals("username")) { String userName = cookie.getValue(); out.println("Hello " + userName); break; } }
So, imagine that Kim wants to put up a form that asks the user to submit his name. The form calls a servlet that gets the username request parameter, and uses the name value to set a cookie in the response.
The next time this user makes a request on ANY servlet in this web app, the cookie comes back with the request (assuming the cookie is still alive, based on the cookie’s maxAge value). When a servlet in the web app sees this cookie, it can put the user’s name into the dynamically-generated response, and the business logic knows not to ask the user to input his name again.
This code is a simplified test version of the scenario we just described.
Servlet that creates and SETS the cookie
JSP to render the view from this servlet
<html><body> <a href="checkcookie.do">click here</a> </body></html>
You don’t have to know ALL the cookie methods.
For the exam, you don’t have to memorize every one of the methods in class Cookie, but you must know the request and response methods to get and add Cookies. You should also know the Cookie constructor and the getMaxAge() and setMaxAge() methods.
Don’t confuse Cookies with headers!
When you add a header to a response, you pass the name and value Strings as arguments:
response.addHeader("foo", "bar");
But when you add a Cookie to the response, you pass a Cookie object. You set the Cookie name and value in the Cookie constructor.
Cookie cookie = new Cookie("name", name); response.addCookie(cookie);
And remember, too, that there’s both a setHeader() and an addHeader() method (addHeader adds a new value to an existing header, if there is one, but setHeader replaces the existing value). But there’s NOT a setCookie() method. There’s only an addCookie() method!
Highlights of the important moments in an HttpSession object’s life:
The session is created or destroyed.
Session attributes are added, removed, or replaced by other parts of the app.
The session is passivated in one VM and activated in another within a distributed app.
Event and Listener type | |
---|---|
Lifecycle
| |
Attributes
| |
Migration
|
[6] Some distributed apps use leasing as a way for the server to know when a client is gone. The client gets a lease from the server, and then must renew the lease at specified intervals to tell the server that the client is still alive. If the client’s lease expires, the server knows it can destroy any resources it was holding for that client.