2

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:

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

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

  3. 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?

  4. Is there a better way?

Thanks for your help,

Gene

Eyal
  • 3,412
  • 1
  • 44
  • 60
Gene Golovchinsky
  • 6,101
  • 7
  • 53
  • 81
  • There's a [related question](http://stackoverflow.com/questions/2433321/how-to-inject-dependencies-into-httpsessionlistener-using-spring) that has a more "Springy" solution to it. – Eyal May 12 '13 at 17:06

1 Answers1

2

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?

Gene Golovchinsky
  • 6,101
  • 7
  • 53
  • 81