I am writing an web application that allows people to collaborate. I would like to have some of my services scoped to the collaboration (which involves a few people) rather than to any individual http session. I created a custom Scope
that stores the beans. To manage the bean lifecycle, I keep track of the session ids associated as follows:
protected ConcurrentMap<String,Object> attributes =
new ConcurrentHashMap<String, Object>();
...
@Override
public Object get(String name, ObjectFactory<?> factory) {
synchronized(this.attributes) {
Object scopedObject = this.attributes.get(name);
if (scopedObject == null) {
scopedObject = factory.getObject();
this.attributes.put(name, scopedObject);
RequestAttributes reqAttrs = RequestContextHolder.currentRequestAttributes();
activeSession(name).add(reqAttrs.getSessionId());
}
return scopedObject;
}
}
When a session closes, I would like to remove the session id from the list of active sessions associated with a given bean name. When set becomes empty, I can clean up.
The easiest way I can think of the manage session closing is with an HttpSessionListener
, but I have a disconnect between my Scope
and the listener. I see the following possibilities:
I can create the HttpSessionListener
statically, assume a sole instance, have it manage a subscription list, and have my Scope
instances subscribe to its events. But that seems redundant, and I don't like the singleton pattern for this.
If I had access to the HttpSession
in the Scope
, I could add the Scope
to a list stored in the session, and have the listener notify the members of that list that the session is going away. But I don't see how to get my hands on the session object (rather than just its id) in the Scope
instance.
I can make my Scope
implement the HttpSessionListener
interface and thereby update its state directly, but I don't know how to register a listener programmatically. Is there a public way of doing that?
Is there a better way?
Thanks for your help,
Gene
Not having received any comments or answers, I went with option #1, as follows:
public class SessionMonitor implements HttpSessionListener {
protected final Log logger = LogFactory.getLog(getClass());
protected CopyOnWriteArrayList<SessionEventListener> subscribers = new CopyOnWriteArrayList<SessionEventListener>();
protected ConcurrentHashMap<String,HttpSession> sessions = new ConcurrentHashMap<String,HttpSession>();
protected static SessionMonitor singleton;
public static SessionMonitor soleInstance() throws ConfigurationException {
if (singleton == null)
throw new ConfigurationException("No SessionMonitor instance has been created");
return singleton;
}
public SessionMonitor() {
if (singleton == null)
singleton = this;
}
@Override
public void sessionCreated(HttpSessionEvent e) {
HttpSession session = e.getSession();
this.sessions.putIfAbsent(session.getId(), session);
logger.trace("Registered session " + session.getId());
}
@Override
public void sessionDestroyed(HttpSessionEvent e) {
String sessionId = e.getSession().getId();
this.sessions.remove(sessionId);
for (SessionEventListener listener: subscribers)
listener.sessionEnded(sessionId);
logger.trace("Removed session " + sessionId);
}
public HttpSession getSession(String id) {
return this.sessions.get(id);
}
public void addListener(SessionEventListener listener) {
this.subscribers.add(listener);
logger.trace("Added listener " + listener);
}
public void removeListener(SessionEventListener listener) {
this.subscribers.remove(listener);
logger.trace("Removed listener " + listener);
}
}
When the scope gets created, it registers itself with the SessionMonitor
:
public ConditionalScope() throws ConfigurationException {
logger.debug("Registering " + this.toString() + " for session monitoring");
SessionMonitor.soleInstance().addListener(this);
}
I am not clear, however, on when to remove the Scope
from the SessionMonitor
. Would some sort of a WeakArray
work here?