You can do sessions even if the client doesn’t accept cookies, but you have to do a little more work...

image with no caption

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.

Note

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.

URL rewriting: something to fall back on

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.

image with no caption

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.

image with no caption
image with no caption

URL rewriting kicks in ONLY if cookies fail, and ONLY if you tell the response to encode the URL

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.

image with no caption

Q:

Q: Wait a minute... how DOES the Container know that cookies aren’t working? At what point does the Container decide to use URL rewriting?

A:

A: A really dumb Container doesn’t care whether cookies work or not—the dumb Container will always attempt to send the cookie AND do URL rewriting each time, even if cookies are working. But here’s how a decent Container handles it:

When the Container sees a call to getSession(), and the Container didn’t get a session ID with the client’s request, the Container now knows that it must attempt to start a new session with the client. At this point, the Container doesn’t know if cookies will work, so with this first response back to the client, it tries BOTH cookies and URL rewriting.

Q:

Q: Why can’t it try cookies first... and do URL rewriting on the next response if it doesn’t get back a cookie?

A:

A: Remember, if the Container doesn’t get a session ID from the client, the Container won’t even KNOW that this is the next request from that client. The Container won’t have any way to know that it tried cookies the last time, and they didn’t work. Remember, the ONLY way the Container can recognize that it has seen this client before is if the client sends a session ID!

So, when the Container sees you call request.getSession(), and realizes it needs to start a new session with this client, the Container sends the response with both a “Set-Cookie” header for the session ID, and the session ID appended to the URLs (assuming you used response.encodeURL()).

Now imagine the next request from this client—it will have the session ID appended to the request URL, but if the client accepts cookies, the request will ALSO have a session ID cookie. When the servlet calls request.getSession(), the Container reads the session ID from the request, finds the session, and thinks to itself, “This client accepts cookies, so I can ignore the response.encodeURL() calls. In the response, I’ll send a cookie since I know that works, and there’s no need for any URL rewriting, so I won’t bother...”

URL rewriting works with sendRedirect()

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")

Q:

Q: What about all my static HTML pages... they are full of <a href> links. How do I do URL rewriting on those static pages?

A:

A: You can’t! The only way to use URL rewriting is if ALL the pages that are part of a session are dynamically-generated! You can’t hard-code session ID’s, obviously, since the ID doesn’t exist until runtime. So, if you depend on sessions, you need URL rewriting as a fall-back strategy. And since you need URL rewriting, you have to dynamically generate the URLs in the response HTML! And that means you have to process the HTML at runtime.

Yes, this is a performance issue. So you must think very carefully about the places where sessions matter to your app, and whether sessions are critical to have or merely good to have.

Q:

Q: You said that to use URL rewriting, pages must be dynamically-generated, so does this mean I can do it with JSPs?

A:

A: Yes! You can do URL-rewriting in a JSP, and there’s even a simple JSTL tag that makes it easy, <c:URL>, that you’ll see when you get to the chapter on using custom tags.

Q:

Q: Is URL rewriting handled in a vendor-specific way?

A:

A: Yes, URL rewriting is handled in a vendor-specific way. Tomcat uses a semicolon “;” to append the extra info to the URL. Another vendor might use a comma or something else. The point is, whatever the Container uses as the separator is recognized by the Container when a request comes in. So when the Container sees the separator that it uses (in other words, the separator that it added during URL rewriting), it knows that everything after that is “extra info” that the Container put there. In other words, the Container knows how to recognize and parse the extra stuff it (the Container) appended to the URL.

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.

Note

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.

Note

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:

image with no caption

And you shouldn’t see a custom “jsessionid” header in a request or response:

image with no caption

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).

Getting rid of sessions

image with no caption

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?

Flex Your Mind

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.

How we want it to work...

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.)

  1. image with no caption
  2. image with no caption
  3. image with no caption

The HttpSession interface

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

Key HttpSession 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.

Flex Your Mind

Now that you’ve seen these methods, can you put together a strategy for eliminating abandoned sessions?

Setting session timeout

image with no caption

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)

  1. 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.

    image with no caption
  2. 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):

    image with no caption

Note

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.

Can I use cookies for other things, or are they only for sessions?

image with no caption

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!

image with no caption

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.

Using Cookies with the Servlet API

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.

image with no caption

Creating a new Cookie

image with no caption

Setting how long a cookie will live on the client

image with no caption

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;
    }
}

Note

There’s no getCookie(String) method... you can only get cookies in a Cookie array, and then you have to loop over the array to find the one you want.

Simple custom cookie example

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

image with no caption

JSP to render the view from this servlet

<html><body>
 <a href="checkcookie.do">click here</a>
</body></html>

Note

OK, sure, there’s nothing JSP-ish about this, but we hate outputting even THIS much HTML from a servlet. The fact that we’re forwarding to a JSP doesn’t change the cookie setting. The cookie is already in the response by the time the request is forwarded to the JSP...

Custom cookie example continued...

Servlet that GETS the cookie

image with no caption

Relax

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.

Note

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!

Key milestones for an HttpSession

Highlights of the important moments in an HttpSession object’s life:

The session is created or destroyed.

image with no caption

Session attributes are added, removed, or replaced by other parts of the app.

image with no caption

The session is passivated in one VM and activated in another within a distributed app.

image with no caption

Session lifecycle Events

Milestone

Event and Listener type

Lifecycle

  • The session was created

    When the Container first creates a session. At this point, the session is still considered new (in other words, the client has not yet sent a request with the session ID).

    The session was destroyed

    When the Container invalidates a session (because the session timed out or some part of the application called the session’s invalidate() method).

Attributes

  • An attribute was added

    When some part of the app calls setAttribute() on the session.

    An attribute was removed

    When some part of the app calls removeAttribute() on the session.

    An attribute was replaced

    When some part of the app calls setAttribute() on the session, and the name of the attribute has already been bound to the session.

Migration

  • The session is about to be passivated

    When the Container is about to migrate (move) the session into a different VM. Called before the session moves, so that attributes have a chance to prepare themselves for migration.

    The session has been activated

    When the Container has just migrated (moved) the session into a different VM. Called before any other part of the app can call getAttribute() on the session, so the just-moved attributes have a chance to get themselves ready for access.



[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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset