4

こんばんは、

テストJSF2.0Webアプリで、アクティブなセッションの数を取得しようとしていますが、HttpSessionListenerのsessionDestroyedメソッドに問題があります。実際、ユーザーがログインすると、アクティブなセッションの数は1増加しますが、ユーザーがログオフすると、同じ数がそのまま残り(デクリメントは発生しません)、さらに悪いことに、同じユーザーが再度ログインすると(彼がセッションを検証しなかったとしても)、同じ数が増加します。別の言葉で言えば:

1-ログインすると、アクティブなセッション数が1ずつ増加します。2-Iログアウト(セッションが検証されなくなります)3-再度ログインすると、セッション数が1ずつ増加します。表示は=2です。4-繰り返します。ログインしているユーザーが1人だけである間、セッション数は増加し続けます。

そのため、メソッド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}"/> 

別のmanagedBeanから:

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
4

3 に答える 3

10

わからない。これは、1人の訪問者には問題なく機能するようです。しかし、いくつかのことは間違いなくあなたの中で正しく見えませんHttpSessionListener


@ManagedBean
public class SessionEar implements HttpSessionListener {

なんで@ManagedBean?それは意味がありません、それを削除します。Java EE 6では、@WebListener代わりに使用します。


    BufferedWriter output = null;

これは間違いなくインスタンス変数であってはなりません。スレッドセーフではありません。それをmethodlocalと宣言します。すべてHttpSessionListenerの実装について、アプリケーションの存続期間を通じてインスタンスは1つだけです。セッションの作成/破棄が同時に行わoutputれると、ビジー状態のときに別のセッションによってオーバーライドされ、ファイルが破損します。


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

それらもインスタンス変数であってはなりません。新しいセッションを作成するたびにそれが上書きされ、他のすべてのユーザーのプレゼンテーションに反映されます。つまり、スレッドセーフではありません。それらを取り除き、何らかの理由でそれを取得する必要がある場合は、ELで#{session.creationTime}使用します。または、HTTPリクエスト内#{session.maxInactiveInterval}のインスタンスから直接取得します。HttpSession


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

これはメソッド内では常に当てはまります。sessionCreated()これは意味がありません。それを除く。


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

そのメソッドが正確に何をしているのかはわかりませんが、セッションが作成または破棄されようとしているときにスレッドにが存在するという保証はないことを警告したいと思います。FacesContextJSF以外のリクエストで発生する可能性があります。または、HTTPリクエストの手段がまったくない場合もあります。したがって、NPEのリスクがFacesContextありnullます。


それでも、次のテストスニペットを作成しましたが、問題なく動作します。@SessionScopedBeanは暗黙的にセッションを作成します。コマンドボタンはセッションを無効にします。すべてのメソッドは期待どおりに呼び出されます。同じブラウザタブでボタンを何回押すと、カウントは常に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;
    }

}

<listener>(Java EE 5ではweb.xml、通常の方法で登録する必要があることに注意してください)

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

上記の例がうまくいく場合、問題は別の場所にある可能性があります。<listener>おそらく、それをまったく登録しておらずweb.xml、ログインメソッド内で毎回リスナーの新しいインスタンスを手動で作成しているだけです。とにかく、これで少なくとも、さらに構築するための最小限のキックオフの例があります。

于 2011-06-10T20:49:22.483 に答える
3

まったく異なる方向への何か-tomcatはJMXをサポートしています。アクティブなセッションの数を示すJMXMBeanがあります。(コンテナがTomcatでない場合でも、JMXをサポートし、それを追跡する方法を提供する必要があります)

于 2011-06-10T22:10:50.390 に答える
1

あなたpublic void sessionDestroyed(HttpSessionEvent se) {は呼ばれていますか?なぜ増加しないのかわかりません。ユーザーがsession.invalidate()ログアウトを介して呼び出した後、セッションは破棄され、次のリクエストのために新しいセッションが作成されます。これは正常な動作です。

于 2011-06-10T20:45:38.417 に答える