Domanda

Buona sera,

In un test JSF 2.0 web app, sto cercando di ottenere il numero di sessioni attive, ma c'è un problema nel metodo sessionDestroyed del HttpSessionListener. Infatti, quando un utente accede, aumenta il numero di sessioni attive da 1, ma quando un utente si disconnette, lo stesso numero rimane invariata (senza desincrementation accade) e il peggio è che, quando lo stesso utente accede nuovamente ( anche se lui non convalidato la sessione), lo stesso numero viene incrementato. Per mettere che con parole diverse:

1- accedo, il numero di sessioni attive viene incrementato di 1. 2- I Logout (la sessione ottiene non convalidato) 3- entrare ancora, il numero sessioni viene incrementato di 1. Il display è = 2. 4- Ripeto l'operazione, e il numero di sessioni continua ad essere incrementato, mentre c'è un solo utente connesso.

Così ho pensato che il metodo sessionDestroyed non si chiama correttamente, o forse in modo efficace chiamato dopo il timeout di sessione che è un parametro in web.xml (il mio è di 60 minuti). Questo è strano come questo è un listener della sessione e non c'è niente di sbagliato con la mia classe.

Se qualcuno si prega di avere un indizio?

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

sto recuperando il numero di sessioni attive in questo modo:

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

da un altro managedBean:

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

Il mio metodo di disconnessione è la seguente:

 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
È stato utile?

Soluzione

Non sono sicuro. Questo sembra funzionare bene per un singolo visitatore. Ma alcune cose sicuramente non guardare a destra nel vostro HttpSessionListener.


@ManagedBean
public class SessionEar implements HttpSessionListener {

Perché è un @ManagedBean? Non ha senso, rimuoverlo. In Java EE 6 usereste @WebListener .


    BufferedWriter output = null;

Questo dovrebbe sicuramente non essere una variabile di istanza. Non è threadsafe. Dichiararla methodlocal. Per ogni applicazione HttpSessionListener c'è solo una esempio per tutta la vita dell'applicazione. Quando ci sono simultanei creazioni di sessione / distrugge, allora il vostro output ottenere sovrascritto da un altro mentre occupato e il file otterrebbero danneggiato.


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

Quelli dovrebbe anche non essere una variabile di istanza. Ogni nuova creazione sessione sarebbe ignorare e che sarebbe ottenere riflessa nella presentazione di tutti gli altri utenti. Cioè non è threadsafe. Sbarazzarsi di loro e fare uso di #{session.creationTime} e #{session.maxInactiveInterval} in EL, se avete bisogno di ottenere laggiù per qualche motivo. O semplicemente ottenere direttamente dalla dell'istanza HttpSession all'interno di una richiesta HTTP.


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

Questa è sempre vero metodo all'interno sessionCreated(). Questo non ha senso. Rimuoverlo.


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

Non so esattamente cosa sta facendo quel metodo, ma voglio solo mettere in guardia che non v'è alcuna garanzia che il FacesContext è presente nel thread quando la sessione sta per essere creato o distrutto. Esso può avvenire in una richiesta non JSF. O ci può essere alcun mezzo di una richiesta HTTP a tutti. Così si rischia di NPE perché il FacesContext è null allora.


Ciò nonostante, ho creato il seguente frammento di prova e funziona bene per me. Il fagiolo @SessionScoped crea implicitamente la sessione. Il CommandButton invalida la sessione. Tutti i metodi sono chiamati come previsto. Quante volte si preme anche il tasto nella stessa scheda del browser, il conteggio è sempre 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();
    }

}

e

@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;
    }

}

(nota a Java EE 5 è necessario registrarsi come <listener> in web.xml solito modo)

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

Se l'esempio precedente funziona per voi, allora il vostro problema probabili bugie da qualche altra parte. Forse non si è registrato come <listener> in web.xml a tutti e si sta semplicemente creando manualmente una nuova istanza della ogni ascoltatore all'interno di alcuni metodo di login. Indipendentemente da ciò, ora avere almeno un esempio di minima calcio d'inizio per costruire ulteriormente.

Altri suggerimenti

Qualcosa in una direzione completamente diversa - Tomcat supporti JMX. C'è un JMX MBean che vi dirà il numero di sessioni attive. (Se il contenitore non è Tomcat, dovrebbe ancora sostenere JMX e fornire un modo per tenere traccia di quello)

È il vostro public void sessionDestroyed(HttpSessionEvent se) { chiamato? Non vedo motivo per cui non viene incrementato. Dopo l'utente chiama session.invalidate() attraverso logout, la sessione viene distrutta, e per la successiva richiesta viene creata una nuova. Questo è un comportamento normale.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top