Problema obteniendo usuarios online en Spring

Hola amigos, os cuento, lo que intento es cargar desde un SessionListener el valor de los usuarios online a un bean contador, de tal manera, que después es accedido a través del JSP via JSTL.

Para ello, lo primero que creo es el bean contador.

public class OnlineUsers {

    private int usuarios = 0;

    public int getUsuarios() {
        return this.usuarios;
    }

    public void setUsuarios(int usuarios) {
        this.usuarios = usuarios;
    }

}

<bean name="usuariosOnline" class="com.programacionj2ee.beans.OnlineUsers" />

Lo siguiente, el SessionListener con acceso al bean contador cargado en el contexto de la aplicación.

public class SessionListener implements HttpSessionListener {

    private List sessions = new ArrayList();
    private ApplicationContext context;
    private OnlineUsers onlineUsers;

    /*public SessionListener() {
    ApplicationContext context = MyContext.getApplicationContext();
    this.onlineUsers = (OnlineUsers) context.getBean("usuariosOnline");
    }*/

    public void sessionCreated(HttpSessionEvent event) {
        System.out.println("SESION CREADA.");
        HttpSession session = event.getSession();
        sessions.add(session.getId());

        //session.setAttribute("counter", sessions.size());
        this.context = MyContext.getApplicationContext();
        this.onlineUsers = (OnlineUsers) context.getBean("usuariosOnline");
        onlineUsers.setUsuarios(sessions.size());
    }

    public void sessionDestroyed(HttpSessionEvent event) {
        System.out.println("SESION CERRADA.");
        HttpSession session = event.getSession();
        sessions.remove(session.getId());

        //session.setAttribute("counter", sessions.size());
        this.context = MyContext.getApplicationContext();
        this.onlineUsers = (OnlineUsers) context.getBean("usuariosOnline");
        onlineUsers.setUsuarios(sessions.size());
    }

    public int getActiveSessionNumber() {
        return sessions.size();
    }
}

public class MyApplicationContextAware implements ApplicationContextAware {

    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        MyContext.setApplicationContext(ctx);
    }
}

<bean id="myApplicationContextAware" class="com.programacionj2ee.context.MyApplicationContextAware" />
public class MyContext {

    private static ApplicationContext context;

    public static void setApplicationContext(ApplicationContext applicationContext) {
        context = applicationContext;
    }

    public static ApplicationContext getApplicationContext() {
        return context;
    }
}

Añado el listener al web.xml para que sera cargado en el deploy.

    <listener>
        <listener-class>com.programacionj2ee.listeners.SessionListener</listener-class>
    </listener>

Y accedo a la propiedad que retorna el número de usuarios del bean contador vía JSTL.

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
...
<c:out value="${usuariosOnline.usuarios}" />

Haciendo debug en los métodos sessionCreated y sessionDestroyed funcionan perfectamente, y el valor en cada momento del bean contador es correcto. El PROBLEMA esta en que directamente, en la vista no aparece ningún resultado, blanco.

¿Alguien sabe donde reside el problema?. Gracias.

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.
Imagen de ezamudio

enredos

Pues a mi me parece que tu bronca es que en el JSP no tienes el bean usuariosOnline, porque no lo has metido a la pagina. Como que le das muchas vueltas al asunto de tener el applicationContext, no? Por qué no usas el ContextLoaderListener de Spring?

        <listener>
                <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>

Con eso puedes obtener el applicationContext desde un servlet muy fácil:

public void init(ServletConfig conf) throws ServletException {
  super.init();
  ApplicationContext ctxt = WebApplicationContextUtils.getRequiredWebApplicationContext(conf.getServletContext());
  //luego puedes obtener los beans que necesitas, total el servlet siempre es la misma instancia
}

Tiene mucho tiempo que no uso JSP, la última vez todavía era con Spring 2.5 y fue al aventón, pero la manera en que usaba el appcontext en JSP era asi:

<%@page import="org.springframework.web.context.ContextLoader"%>
<%
ApplicationContext ctxt = ContextLoader.getCurrentWebApplicationContext().getBean("userDao");
//Sacar los beans que necesitas por ejemplo...
OnlineUsers users = (OnlineUsers)ctxt.getBean("usuariosOnline");
%>
<c:out value="${users.usuarios}" />

Aparte de esto, ese bean que cuenta los usuarios, si lo usas de manera concurrente, no va a darte un valor fidedigno. En vez de set y get con un vil int, deberías tener un AtomicInteger y publicar tres metodos en tu bean. Algo como entrar(), salir() y getUsuarios() para que entrar() incremente el número por 1, salir() decremente el número por 1 y getUsuario() devuelve el valor actual. AtomicInteger sirve justo para lo que quieres en ambientes concurrentes.

Imagen de ezamudio

Spring 3

No sé si estaba en Spring 2 pero en Spring 3 ya vi que puedes usar un objeto llamado InternalResourceViewResolver para exponer los beans del contexto en un JSP 2.0. Si usas JSTL, puedes agregar esto a tu applicationContext:

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
   <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
</bean>

Y entonces en tus JSP ya simplemente puedes invocar cualquier bean de tu applicationContext con ${bean} y o con el <c:out> de JSTL. Y no tienes que implementar nada más. Tu SessionListener lo sigues teniendo pero usa mucho menos código, porque el applicationContext simplemente lo obtienes con WebApplicationContextUtils.getRequiredWebApplicationContext(session.getServletContext()) de modo que no necesitas esa clase MyContext ni la de MyApplicationContextAware.

Hola de nuevo.

Hola ezamudio, y gracias por contestar.

WebApplicationContextUtils.getRequiredWebApplicationContext(session.getServletContext()) no lo puedo utilizar en Listener ya que no tengo forma de acceder a la session de la aplicación al no tener acceso al objeto HttpServletRequest, y a través de event.getSession.getServletContext() me dice que no se encuentra el bean "usuariosOnline" en ese contexto de la aplicación.

PD: Respecto a lo de meter código java en un JSP, soy enemigo total de eso... Para eso tenemos los beans en Spring y JSTL, que sino los diseñadores se asustan... xD

Respecto al problema en particular, sigo sin solucionarlo, he añadido la propiedad "org.springframework.web.servlet.view.JstlView" al viewResolver pero sigue sin funcionar.

Lo que es el SessionListener y el bean contador, funcionan perfectamente, ya que al hacer debug a los métodos del SessionListener, y meterle unos println los datos son correctos, es mas, si envió los datos a la sesión y los recupero en el JSP con "sessionScope.contador" funciona perfectamente. El problema reside unicamente en el acceso desde el JSP a la propiedad de ese maldito bean, no la hace bien y no se porque.

Solucionado.

Hola, nada, era tan sencillo como poner la propiedad exposeContextBeansAsAttributes a true en el viewResolver.

Un saludo y gracias. :)