Счетчик активных сессий JSF. Как? - PullRequest
4 голосов
/ 11 июня 2011

Добрый вечер,

В тестовом веб-приложении JSF 2.0 я пытаюсь получить количество активных сеансов, но есть проблема в методе sessionDestroyed объекта HttpSessionListener.Действительно, когда пользователь входит в систему, количество активных сеансов увеличивается на 1, но когда пользователь выходит из системы, остается то же самое число, что и есть (десинкрементация не происходит), и хуже всего то, что когда этот же пользователь снова входит в систему (несмотря на то, что он аннулировал сеанс), то же число увеличивается.Другими словами:

1 - Я вхожу в систему, номер активных сеансов увеличивается на 1. 2 - Выйду из системы (сеанс становится неподтвержденным) 3 - Я снова вхожу в систему, номер сессии увеличивается на1. На дисплее отображается = 2. 4- Я повторяю операцию, и число сеансов продолжает увеличиваться, в то время как в систему вошел только один пользователь.

Поэтому я подумал, что метод sessionDestroyed не вызывается должным образомможет быть эффективно вызван после тайм-аута сеанса, который является параметром в WEB.XML (у меня это 60 минут).Это странно, так как это слушатель сеанса, и с моим классом все в порядке.

Кто-нибудь может подсказать?

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

Я получаю номер активного сеанса следующим образом:

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

из другого управляемого элемента:

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

Мой метод выхода из системы следующий:

 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

Ответы [ 3 ]

10 голосов
/ 11 июня 2011

Я не уверен. Кажется, это работает нормально для одного посетителя. Но некоторые вещи определенно не выглядят правильно в вашем HttpSessionListener.


@ManagedBean
public class SessionEar implements HttpSessionListener {

Почему это @ManagedBean? Нет смысла, убери это. В Java EE 6 вместо этого вы бы использовали @WebListener.


    BufferedWriter output = null;

Это определенно не должно быть переменной экземпляра. Это не потокобезопасно. Объявите это методом локально. Для каждой реализации HttpSessionListener существует только один экземпляр в течение всего времени жизни приложения. При одновременном создании / удалении сеанса ваш output будет перезаписан другим, когда он занят, и ваш файл будет поврежден.


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

Они также не должны быть переменной экземпляра. Каждое новое создание сеанса будет переопределять его, и это будет отражено в презентации всех других пользователей. То есть это не потокобезопасно. Избавьтесь от них и используйте #{session.creationTime} и #{session.maxInactiveInterval} в EL, если вам нужно получить его там по какой-то причине. Или просто получите его прямо из экземпляра HttpSession в HTTP-запросе.


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

Это всегда true внутри sessionCreated() метода. Это не имеет никакого смысла. Удалить его.


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

Я не знаю, что именно делает этот метод, но я просто хочу предупредить, что нет гарантии , что FacesContext присутствует в потоке, когда собирается создать сеанс или уничтожен. Это может иметь место в запросе не-JSF. Или может не быть никаких средств для HTTP-запроса вообще. Таким образом, вы рискуете NPE, потому что FacesContext равно null тогда.


Тем не менее, я создал следующий тестовый фрагмент, и он отлично работает для меня. Бин @SessionScoped неявно создает сеанс. Кнопка команды делает сеанс недействительным. Все методы вызываются как и ожидалось. Сколько раз вы также нажимаете кнопку на той же вкладке браузера, счетчик всегда равен 1.

<h:form>
    <h:commandButton value="logout" action="#{bean.logout}" />
    <h:outputText value="#{bean.sessionCount}" />
</h:form>
* * С тысячей сорок-девять
@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();
    }

}

и

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

}

(обратите внимание, что в Java EE 5 необходимо зарегистрировать его как <listener> в web.xml обычным способом)

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

Если приведенный выше пример работает для вас, то, скорее всего, ваша проблема лежит в другом месте. Возможно, вы вообще не зарегистрировали его как <listener> в web.xml, и вы просто вручную создаете новый экземпляр слушателя каждый раз в каком-либо методе входа в систему. Как бы то ни было, теперь у вас есть хотя бы минимальный стартовый пример для дальнейшего развития.

3 голосов
/ 11 июня 2011

Что-то в совершенно другом направлении - Tomcat поддерживает JMX.Существует JMX MBean, который сообщит вам количество активных сессий.(Если ваш контейнер не является tomcat, он все равно должен поддерживать JMX и предоставлять какой-то способ его отслеживания)

1 голос
/ 11 июня 2011

Твой public void sessionDestroyed(HttpSessionEvent se) { называется? Я не понимаю, почему это не будет увеличиваться. После того, как пользователь вызывает session.invalidate() через выход из системы, сеанс уничтожается, и для следующего запроса создается новый. Это нормальное поведение.

...