/*******************************************************************************
* Gisgraphy Project
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
*
* Copyright 2008 Gisgraphy project
* David Masclet <davidmasclet@gisgraphy.com>
*
*
*******************************************************************************/
package com.gisgraphy.webapp.listener;
import java.util.LinkedHashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionBindingEvent;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import com.gisgraphy.model.User;
/**
* UserCounterListener class used to count the current number of active users
* for the applications. Does this by counting how many user objects are stuffed
* into the session. It Also grabs these users and exposes them in the servlet
* context.
*
* @author <a href="mailto:matt@raibledesigns.com">Matt Raible</a>
*/
public class UserCounterListener implements ServletContextListener,
HttpSessionAttributeListener {
/**
* Name of user counter variable
*/
public static final String COUNT_KEY = "userCounter";
/**
* Name of users Set in the ServletContext
*/
public static final String USERS_KEY = "userNames";
/**
* The default event we're looking to trap.
*/
public static final String EVENT_KEY = HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY;
private transient ServletContext servletContext;
private int counter;
private Set<User> users;
/**
* Initialize the context
*
* @param sce
* the event
*/
public synchronized void contextInitialized(ServletContextEvent sce) {
servletContext = sce.getServletContext();
servletContext.setAttribute((COUNT_KEY), Integer.toString(counter));
}
/**
* Set the servletContext, users and counter to null
*
* @param event
* The servletContextEvent
*/
public synchronized void contextDestroyed(ServletContextEvent event) {
servletContext = null;
users = null;
counter = 0;
}
synchronized void incrementUserCounter() {
counter = Integer.parseInt((String) servletContext
.getAttribute(COUNT_KEY));
counter++;
servletContext.setAttribute(COUNT_KEY, Integer.toString(counter));
}
synchronized void decrementUserCounter() {
int counter = Integer.parseInt((String) servletContext
.getAttribute(COUNT_KEY));
counter--;
if (counter < 0) {
counter = 0;
}
servletContext.setAttribute(COUNT_KEY, Integer.toString(counter));
}
@SuppressWarnings("unchecked")
synchronized void addUsername(User user) {
users = (Set) servletContext.getAttribute(USERS_KEY);
if (users == null) {
users = new LinkedHashSet<User>();
}
if (!users.contains(user)) {
users.add(user);
servletContext.setAttribute(USERS_KEY, users);
incrementUserCounter();
}
}
@SuppressWarnings("unchecked")
synchronized void removeUsername(User user) {
users = (Set<User>) servletContext.getAttribute(USERS_KEY);
if (users != null) {
users.remove(user);
}
servletContext.setAttribute(USERS_KEY, users);
decrementUserCounter();
}
/**
* This method is designed to catch when user's login and record their name
*
* @see javax.servlet.http.HttpSessionAttributeListener#attributeAdded(javax.servlet.http.HttpSessionBindingEvent)
* @param event
* the event to process
*/
public void attributeAdded(HttpSessionBindingEvent event) {
if (event.getName().equals(EVENT_KEY) && !isAnonymous()) {
SecurityContext securityContext = (SecurityContext) event
.getValue();
if (securityContext.getAuthentication().getPrincipal() instanceof User) {
User user = (User) securityContext.getAuthentication()
.getPrincipal();
addUsername(user);
}
// Workaround for Jetty bug
// (http://www.nabble.com/current-user-count-incorrect-tf3550268.html#a9919134)
} else if (event
.getName()
.equals(
"SPRING_SECURITY_LAST_USERNAME")) {
String username = (String) event.getValue();
User user = new User(username);
addUsername(user);
}
}
private boolean isAnonymous() {
AuthenticationTrustResolver resolver = new AuthenticationTrustResolverImpl();
SecurityContext ctx = SecurityContextHolder.getContext();
if (ctx != null) {
Authentication auth = ctx.getAuthentication();
return resolver.isAnonymous(auth);
}
return true;
}
/**
* When user's logout, remove their name from the hashMap
*
* @see javax.servlet.http.HttpSessionAttributeListener#attributeRemoved(javax.servlet.http.HttpSessionBindingEvent)
* @param event
* the session binding event
*/
public void attributeRemoved(HttpSessionBindingEvent event) {
if (event.getName().equals(EVENT_KEY) && !isAnonymous()) {
SecurityContext securityContext = (SecurityContext) event
.getValue();
Authentication auth = securityContext.getAuthentication();
if (auth != null && (auth.getPrincipal() instanceof User)) {
User user = (User) auth.getPrincipal();
removeUsername(user);
}
}
}
/**
* Needed for Acegi Security 1.0, as it adds an anonymous user to the
* session and then replaces it after authentication.
* http://forum.springframework.org/showthread.php?p=63593
*
* @see javax.servlet.http.HttpSessionAttributeListener#attributeReplaced(javax.servlet.http.HttpSessionBindingEvent)
* @param event
* the session binding event
*/
public void attributeReplaced(HttpSessionBindingEvent event) {
if (event.getName().equals(EVENT_KEY) && !isAnonymous()) {
SecurityContext securityContext = (SecurityContext) event
.getValue();
if (securityContext.getAuthentication() != null) {
if (securityContext.getAuthentication().getPrincipal() instanceof User) {
User user = (User) securityContext.getAuthentication()
.getPrincipal();
addUsername(user);
}
}
}
}
}