Pregunta

Buenas noches,

En una aplicación web de prueba JSF 2.0, estoy tratando de obtener el número de sesiones activas, pero hay un problema en el método de sessionDestrOyed del httpsessionListener. De hecho, cuando un usuario inicia sesión, el número de sesión activa aumenta en 1, pero cuando un usuario se desconecta, el mismo número permanece como lo es (no ocurre la desincrementación) y lo peor es que, cuando el mismo usuario inicia sesión nuevamente ( Aunque no validó la sesión), el mismo número se incrementa. Para poner eso en diferentes palabras:

1- I Iniciar sesión, el número de sesiones activas se incrementa en 1. 2- I INCOGAR (la sesión no se valida) 3- Inicio de nuevo, el número de sesiones se incrementa por 1. La pantalla es = 2. 4- Repito la La operación, y el número de sesiones se sigue incrementando, mientras que solo hay un usuario iniciado.

Así que pensé que el método SessionDestroyed no se llama correctamente, o tal vez efectivamente llamado después del tiempo de espera de la sesión, que es un parámetro en Web.xml (el mío es de 60 minutos). Eso es extraño ya que este es un oyente de sesión y no hay nada de malo en mi clase.

¿Alguien tiene una pista?

package mybeans;

import entities.Users;
import java.io.*;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import jsf.util.JsfUtil;

/**
 * Session Listener.
 * @author TOTO
 */
@ManagedBean
public class SessionEar implements HttpSessionListener {

    public String ctext;
    File file = new File("sessionlog.csv");
    BufferedWriter output = null;
    public static int activesessions = 0;
    public static long creationTime = 0;
    public static int remTime = 0;
    String separator = ",";
    String headtext = "Session Creation Time" + separator + "Session Destruction Time" + separator + "User";

    /**
     * 
     * @return Remnant session time
     */
    public static int getRemTime() {
        return remTime;
    }

    /**
     * 
     * @return Session creation time
     */
    public static long getCreationTime() {
        return creationTime;
    }

    /**
     * 
     * @return System time
     */
    private String getTime() {
        return new Date(System.currentTimeMillis()).toString();
    }

    /**
     * 
     * @return active sessions number
     */
    public static int getActivesessions() {
        return activesessions;
    }

    @Override
    public void sessionCreated(HttpSessionEvent hse) {
        //  Insert value of remnant session time
        remTime = hse.getSession().getMaxInactiveInterval();

        // Insert value of  Session creation time (in seconds)
        creationTime = new Date(hse.getSession().getCreationTime()).getTime() / 1000;
        if (hse.getSession().isNew()) {
            activesessions++;
        } // Increment the session number
        System.out.println("Session Created at: " + getTime());
        // We write into a file information about the session created
        ctext = String.valueOf(new Date(hse.getSession().getCreationTime()) + separator);
        String userstring = FacesContext.getCurrentInstance().getExternalContext().getRemoteUser();

// If the file does not exist, create it
        try {
            if (!file.exists()) {
                file.createNewFile();

                output = new BufferedWriter(new FileWriter(file.getName(), true));
                // output.newLine();
                output.write(headtext);
                output.flush();
                output.close();
            }

            output = new BufferedWriter(new FileWriter(file.getName(), true));
            //output.newLine();
            output.write(ctext + userstring);
            output.flush();
            output.close();
        } catch (IOException ex) {
            Logger.getLogger(SessionEar.class.getName()).log(Level.SEVERE, null, ex);
            JsfUtil.addErrorMessage(ex, "Cannot append session Info to File");
        }

        System.out.println("Session File has been written to sessionlog.txt");

    }

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        // Desincrement the active sessions number
            activesessions--;


        // Appen Infos about session destruction into CSV FILE
        String stext = "\n" + new Date(se.getSession().getCreationTime()) + separator;

        try {
            if (!file.exists()) {
                file.createNewFile();
                output = new BufferedWriter(new FileWriter(file.getName(), true));
                // output.newLine();
                output.write(headtext);
                output.flush();
                output.close();
            }
            output = new BufferedWriter(new FileWriter(file.getName(), true));
            // output.newLine();
            output.write(stext);
            output.flush();
            output.close();
        } catch (IOException ex) {
            Logger.getLogger(SessionEar.class.getName()).log(Level.SEVERE, null, ex);
            JsfUtil.addErrorMessage(ex, "Cannot append session Info to File");
        }

    }
} // END OF CLASS

Estoy recuperando el número de sesiones activas de esta manera:

<h:outputText id="sessionsfacet" value="#{UserBean.activeSessionsNumber}"/> 

De otro ManagedBean:

public String getActiveSessionsNumber() {
        return String.valueOf(SessionEar.getActivesessions());
    }

Mi método de cierre de sesión es el siguiente:

 public String logout() {
        HttpSession lsession = (HttpSession) FacesContext.getCurrentInstance().getExternalContext().getSession(false);
        if (lsession != null) {
            lsession.invalidate();
        }
        JsfUtil.addSuccessMessage("You are now logged out.");
        return "Logout";
    }
    // end of logout
¿Fue útil?

Solución

No estoy seguro. Esto parece funcionar bien para un solo visitante. Pero algunas cosas definitivamente no se ven bien en tu HttpSessionListener.


@ManagedBean
public class SessionEar implements HttpSessionListener {

¿Por qué es un @ManagedBean? No tiene sentido, elimínelo. En Java Ee 6 usarías @WebListener en cambio.


    BufferedWriter output = null;

Esto debería definitivamente no ser una variable de instancia. No es aficionado a los hilos. Declare Methodlocal. Para cada HttpSessionListener implementación solo hay una instancia a lo largo de la vida de la aplicación. Cuando hay creaciones/destrucción de sesión simultáneas, entonces tu output Onte se anule otro mientras está ocupado y su archivo se corrompa.


    public static long creationTime = 0;
    public static int remTime = 0;

Esos tampoco deberían ser una variable de instancia. Cada nueva creación de sesión lo anularía y se reflejaría en la presentación de todos los demás usuarios. Es decir, no es hilo. Deshágase de ellos y utilice #{session.creationTime} y #{session.maxInactiveInterval} en El si necesita obtenerlo allí por alguna razón. O simplemente consíguelo directamente del HttpSession instancia dentro de una solicitud HTTP.


    if (hse.getSession().isNew()) {

Esto es siempre verdadero por dentro sessionCreated() método. Esto no tiene sentido. Retirarlo.


        JsfUtil.addErrorMessage(ex, "Cannot append session Info to File");

No sé qué está haciendo exactamente ese método, pero solo quiero advertir que hay sin garantía que el FacesContext está presente en el hilo cuando la sesión está a punto de ser creada o destruida. Puede tener lugar en una solicitud que no sea JSF. O puede que no haya medios de una solicitud HTTP en absoluto. Entonces corre el riesgo de NPE porque el FacesContext es null después.


No obstante, creé el siguiente fragmento de prueba y funciona bien para mí. los @SessionScoped Bean crea implícitamente la sesión. El CommandButton invalida la sesión. Todos los métodos se llaman como se esperaba. Cuántas veces también presiona el botón en la misma pestaña del navegador, el recuento es siempre 1.

<h:form>
    <h:commandButton value="logout" action="#{bean.logout}" />
    <h:outputText value="#{bean.sessionCount}" />
</h:form>

con

@ManagedBean
@SessionScoped
public class Bean implements Serializable {

    public void logout() {
        System.out.println("logout action invoked");
        FacesContext.getCurrentInstance().getExternalContext().invalidateSession();
    }

    public int getSessionCount() {
        System.out.println("session count getter invoked");
        return SessionCounter.getCount();
    }

}

y

@WebListener
public class SessionCounter implements HttpSessionListener {

    private static int count;

    @Override
    public void sessionCreated(HttpSessionEvent event) {
        System.out.println("session created: " + event.getSession().getId());
        count++;
    }

    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        System.out.println("session destroyed: " + event.getSession().getId());
        count--;
    }

    public static int getCount() {
        return count;
    }

}

(Tenga en cuenta que en Java EE 5 debe registrarse como <listener> en web.xml la forma habitual)

<listener>
    <listener-class>com.example.SessionCounter</listener-class>
</listener>

Si el ejemplo anterior funciona para usted, entonces su problema probablemente se encuentra en otro lugar. Quizás no lo registraras como <listener> en web.xml En absoluto y simplemente estás creando manualmente una nueva instancia del oyente cada vez dentro de algún método de inicio de sesión. De todos modos, ahora al menos tienes un ejemplo de inicio mínimo para construir más adelante.

Otros consejos

Algo en una dirección completamente diferente: Tomcat admite JMX. Hay un JMX Mbean que le dirá la cantidad de sesiones activas. (Si su contenedor no es Tomcat, aún debe admitir JMX y proporcionar alguna forma de rastrear eso)

Es tuyo public void sessionDestroyed(HttpSessionEvent se) { llamó ? No veo por qué no se incrementa. Después de que el usuario llama session.invalidate() A través del cierre de sesión, la sesión se destruye, y para la próxima solicitud se crea una nueva. Este es un comportamiento normal.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top