Ted writes exactly as he talks, so it's funny reading this and hearing his voice read it aloud in my head, as if he were there reading it to me himself .. and I've heard him talk about some of these things using the same words, so it's like deja vu all over again ;-). I have a whole bunch of comments on this chapter, not surprisingly since Java state management for clusters is what Coherence is designed for. However, I thought I'd just start with the HTTP session part, and there are two main comments that I had:
1. Look at the article on TSS labeled
Monitoring Session Replication in J2EE Clusters. The points in this chapter are similar: Keep the HTTP sessions small if you have to use them at all, because the app servers can't manage them (many sessions, large sessions, or combination thereof) efficiently in a distributed environment. Ted is correct on several points, including that sessions should only be used if they are necessary, but isn't that the same as for any feature? On the other hand,
there are very scalable solutions for distributed HTTP session support (sticky or not) including Coherence*Web, which has no problem transparently managing millions of concurrent sessions across hundreds of servers.
2. The use of a filter to implement custom sessions is a neat trick, but it doesn't work for a lot of app servers :-(. We had a precursor to Coherence*Web that did just that (and there are several other companies that tried to do the same thing) but the app servers will often cast the session object to its own internal class. There are two possible results: (1) if you let the app server see its own request/response object, it may create a second session and try to manage that too .. I've seen this on Weblogic for example, and the side-effects cause all sorts of application bugs, and (2) if the app server does get a copy of your session object, it will blow up with class cast exceptions. What does this impact? Well, if you're using declarative security, it impacts that directly .. it just won't work with most app servers. This is one of the complexities (seamless app server support) that took our HTTP session management module from 1,200 lines up to 30,000 lines. Here's how the custom request (substituted by the filter) would create the session:
/**
* Returns the requested HttpSession.
*
* @param fCreate <code>true</code> to create a new session for this request if
* necessary; <code>false</code> to return null if there's
* no current session
*
* @return the requested HttpSession
*/
public HttpSession getSession(boolean fCreate)
{
// get id from cookie
String sId = m_sSessionId;
boolean fNew = false;
// check whether the session has been invalidated
if (sId != null && !getSessionCatalog().containsKey(sId))
{
sId = null;
}
if (sId == null)
{
if (fCreate)
{
m_sSessionId = sId = generateSessionId();
preserveSession(m_response);
fNew = true;
}
else
{
return null;
}
}
CoherenceSession session = instantiateSession(sId);
session.setNew(fNew);
NamedCache catalog = getSessionCatalog();
long lTime = System.currentTimeMillis();
//do the new session operations
if (fNew)
{
catalog.put(sId, null);
session.setCreationTime(lTime);
}
session.setLastAccessedTime(lTime);
session.setMaxInactiveInterval(m_iMaxInActive);
return session;
}
For specific app servers, you can plug in ways to do more custom session management, but it's not portable. For example, in Tomcat, you can implement a session manager, and then you get called to create a session:
/**
* Construct and return a new session object, based on the default
* settings specified by this Manager's properties. The session
* id will be assigned by this method, and available via the getId()
* method of the returned session. If a new session cannot be created
* for any reason, return <code>null</code>.
*
* @exception IllegalStateException if a new session cannot be
* instantiated for any reason
*/
public Session createSession()
{
NamedCache catalog = getSessionCatalog();
Cluster cluster = catalog.getCacheService().getCluster();
int nThread = Thread.currentThread().hashCode();
int nMember = cluster.getLocalMember().getId();
String sDomain = toHexString((nMember <
// find a unique ID; since we use the 1 LSD of the member id & the
// 3 LSD (assuming 16 byte paragraph boundary) of the thread hashcode
// as part of the ID, and the 4 LSD of the time (in cluster time) as
// the other part, the ID should already be unique, but we will
// verify that just in case
String sId = null;
while (sId == null)
{
sId = sDomain + toHexString((int) getClusterTime(), 8);
if (catalog.containsKey(sId))
{
// need to give up the CPU to let a little time pass so the
// next time through the ID will be unique
Thread.yield();
sId = null;
}
}
Session session = instantiateSession(sId);
session.setNew(true);
add(session);
session.setCreationTime(getClusterTime());
return session;
}
It's also worth pointing out that if you are creating these sessions, you're also going to be responsible for distributing them, invalidating them, timing them out, etc. In the end, you are writing a significant chunk of an application server, just to achieve what (IMHO) it should have done out-of-the-box.
Peace,
Cameron Purdy
Tangosol, Inc.Coherence: Shared Memories for J2EE Clusters