Race-Conditions im folgenden Servlet-Code möglich?

Status
Nicht offen für weitere Antworten.

wolfgke

Mitglied
Hallo,
ich habe folgende 2 Codefragmente, die von unterschiedlichen Servlets und damiaufgerufen werden:

Code A:
Java:
HttpSession session = request.getSession(true);

synchronized (session) {
    // mit der Session ein paar Dinge tun
}

und

Code B:
Java:
HttpSession session = request.getSession(false);

if (session != null) {
    synchronized (session) {
        session.invalidate();
    }
}

Ich will innerhalb des synchronized-Blocks von Code A in jedem Fall (!) garantiert haben, dass die Session gültig ist und als Mutex dient (also keine IllegalStateException-Exception geworfen wird).

Nun wäre jedoch (erst einmal rein fiktiv) folgendes Aufrufschema denkbar:

Thread 1 (führt Code A aus): HttpSession session = request.getSession(true);
Thread 2 (führt Code B aus): führt den gesamten Code B aus
Thread 1: führt in Code A den Block
Java:
synchronized (session) {
    // mit der Session ein paar Dinge tun
}
aus

Und voila - plötzlich ist es nicht mehr garantiert, dass die Session innerhalb des synchronized-Blocks von Code A gültig ist.

Nun folgende Fragen:
- Kann das Szenario wirklich so eintreten?
- Wie kann ich dann durch welche Codeänderungen garantieren, dass ich im synchronized-Block von Code tatsächlich eine gültige Session vorliegen habe:
* ein Abfangen von IllegalStateException und falls diese auftrat, es nochmal komplett neu probieren, betrachte ich nur als ultimative Notlösung, da ich dies sehr unelegant finde
* dito im synchronized-Block von Code A noch ein request.getSession(false); aufrufen und wenn ich dann null bekomme, es nochmal komplett neu probieren

Danke im Voraus
wolfgke
 

Noctarius

Top Contributor
Deiner Abfolge nach würde Thread A nach Abarbeitung von Thread B eine invalidated Session als Referenz haben. Kannst aber doch vor dem Betreten des Synchronized Blocks testen ob die Session noch gültig ist und wenn nicht gar nicht erst reinspringen.
 

wolfgke

Mitglied
@Noctarius

Bei deinem Vorschlag sehe ich folgende Probleme:

1. es ist mir keine "richtige" Möglichkeit bekannt, zu überprüfen, ob eine Session gültig ist (nur, wenn
Java:
request.getSession(false);
NULL zurückgibt, dann wissen wir, dass die Session ungültig ist)

2. Angenommen, wir hätten aber eine Möglichkeit dazu (die ich mal mit
Java:
isValid()
als Methode der Klasse HttpSession bezeichnen will). Ich nehme an, du hast folgenden Code im Visier:

Code A:
Java:
HttpSession session = request.getSession(true);

if (session.isValid())
{
    synchronized (session) {
        // mit der Session ein paar Dinge tun
    }
}

Nun könnte aber in diesem Code in Verbindung mit Code B von oben folgende Race-Condition auftreten:

Thread 1 (führt Code A aus): HttpSession session = request.getSession(true);
Thread 1: if (session.isValid()): die Session ist gültig - alles schön
Thread 2: führt komplett Code B aus, Session wird ungültig
Thread 1: betritt synchronized-Block und erlebt ungültige Session

Mir scheint es also, als würde es nicht so einfach funktionieren - es sei denn, ich habe etwas wichtiges übersehen.
 

Noctarius

Top Contributor
Zur Not kannst du auch innerhalb des synchronized auf gültige Session prüfen:

Java:
HttpSession session = request.getSession(true);
    synchronized (session) {
        if (request.getSession(false) != null) {
            // mit der Session ein paar Dinge tun
        }
    }
}

edit: Alternativ

Java:
HttpSession session = request.getSession(true);
    synchronized (session) {
        try {
            session.getValueNames(); // Hier würde die Exception fliegen

            // mit der Session ein paar Dinge tun
        } catch (IllegalStateException e) {
            // Bööööööse :)
        }
    }
}
 
Zuletzt bearbeitet:

byte

Top Contributor
Ich hoffe, Du bist Dir im klaren darüber, dass es keine gute Idee ist, auf die Session zu synchronisieren. Auf diese Weise blockieren sich gleichzeitige Requests doch gegenseitig.
 

wolfgke

Mitglied
In diese Richtung meinte ich, als ich schrieb:

* ein Abfangen von IllegalStateException und falls diese auftrat, es nochmal komplett neu probieren, betrachte ich nur als ultimative Notlösung, da ich dies sehr unelegant finde
* dito im synchronized-Block von Code A noch ein request.getSession(false); aufrufen und wenn ich dann null bekomme, es nochmal komplett neu probieren

Denn: selbst beim neuen Code von dir können böse Race-Conditions auftreten (diesmal benötigen wir jedoch 3 Threads - aber 3 Servlets können jederzeit auftreten - und ich will meinen Code verlässlich haben):

Wir wollen deinen Code wieder mit Code A bezeichnen:
Java:
HttpSession session = request.getSession(true);
    synchronized (session) {
        if (request.getSession(false) != null) {
            // mit der Session ein paar Dinge tun
        }
    }
}

Code B sei wie gehabt.

Thread 1+2 führen Code A aus, Thread 3 Code B.

Thread 1: HttpSession session = request.getSession(true);
Thread 3: führt kompletten Code B aus. Damit ist die Session von Thread 1 ungültig
Thread 2: führt kompletten Code A aus. Damit existiert wieder eine gültige Session im request-Objekt, aber die von Thread 1 bleibt ungültig
Thread 1: synchronisiert über sein (mittlerweile ungültiges) session-Objekt und fragt: "request.getSession(false) != null" ab. Dies ist erfüllt (da durch Thread 2 ein neues gültiges Session-Objekt existiert). Dennoch: im inneren Code bleibt session von Thread 1 ungültig -> Problem


---

Da ich dachte, dass es eine Standard-Aufgabe in Java ist, auf eine Session zuzugreifen, dachte ich, jemand im Forum könnte mir eine einfache elegante Lösung für das Problem liefern. Das Problem scheint also haariger zu sein, als von mir vermutet.

P. S.: Meine aktuelle Lösung ist eine Variante von der oben genannten uneleganten Variante 2 ("im synchronized-Block von Code A noch ein request.getSession(false); aufrufen und wenn ich dann null bekomme, es nochmal komplett neu probieren"), in der Code A folgendermaßen aufgebaut:
Java:
HttpSession session = request.getSession(true);

while (true) {
    synchronized (session) {
        HttpSession session2 = request.getSession(true);
        if (session == session2) {
            // Mit der Session arbeiten
            break;
        } else {
            session = session2;
            continue;
       }
    }
}
Wenn hier Race Conditions auftreten sollten, teilt es mir mit :)
 

wolfgke

Mitglied
@byto

Dass sich Requests dann blockieren, ist mir klar. Absolut unklar ist mir dagegen, wie man es anders machen soll.
 

byte

Top Contributor
@byto

Dass sich Requests dann blockieren, ist mir klar. Absolut unklar ist mir dagegen, wie man es anders machen soll.

Dafür müsste man erstmal wissen, was Du überhaupt machen willst!? Ich nehme mal an, Du hast Dir einen Logout Mechanismus geschrieben, der manuell die Session zerstört (per
Code:
invalidate()
).

Dann würde ich einfach in einem Servlet Filter am Anfang jedes Requests prüfen, ob die Session noch valide ist (siehe
Code:
HttpServletRequest#isRequestedSessionIdValid()
) und im fehlerfall auf eine ensprechende Seite weiterleiten.
 

wolfgke

Mitglied
@byto

Dafür müsste man erstmal wissen, was Du überhaupt machen willst!? Ich nehme mal an, Du hast Dir einen Logout Mechanismus geschrieben, der manuell die Session zerstört (per invalidate()).

Korrekt.

Dann würde ich einfach in einem Servlet Filter am Anfang jedes Requests prüfen, ob die Session noch valide ist (siehe HttpServletRequest#isRequestedSessionIdValid()) und im fehlerfall auf eine ensprechende Seite weiterleiten.

"Mit im Fehlerfall": meinst du damit "wenn eine Exception geworfen wird oder die Session nicht valide ist"? (kein ausschließendes oder). Nur zur Sicherheit, ob ich dich richtig verstanden habe.
 

byte

Top Contributor
Ich meine, wenn die o.g. Methode
Code:
false
liefert.
 

wolfgke

Mitglied
Bei dieser Antwort von dir:

Ich überprüfe also zu Beginn des Requests, ob die Session gültig ist. Wie gehe ich jedoch elegant (!) mit dem Fall um, dass sie während des Requests (dann wenn ich darauf zugreifen will) schon ungültig geworden ist (oder durch eine mittlerweile neue gültige Session ersetzt worden ist)?

Das war der Grund, warum ich mir die Gedanken über Synchronisation gemacht habe (und zu dem weiter oben geschriebenen Ergebnis gekommen bin).
 

byte

Top Contributor
Wenn du invalidate() am Ende Deines Logout Mechanismus aufrufst, ist es extrem unwahrscheinlich, dass ein weiterer Request auf dieser Session offen ist. Sollte es doch mal passieren, dann fliegt in diesem Request ja eine Exception, wenn man versucht, Daten aus der nicht mehr validen Session abzurufen. Diese Exception kannst Du dann ja behandeln, z.B. in einem weiteren Filter.

Ich würde Dir empfehlen, mal einen Blick auf Spring Security zu werfen.
 

wolfgke

Mitglied
Spring Security - ich denke, ich werde es mir, wenn ich Zeit habe, in Zukunft anschauen.

Mein Problem ist eben allgemein, dass ich in Sachen Server Side Java mich in den Bibliotheken noch nicht so gut auskenne (im Moment beschäftige ich mich Hibernate). Jedoch bin ich es aus meinen Programmiererfahrungen mit C++ gewohnt, wenn ich Multithreading verwende, jeden noch so absurden Ablauf-Fall im Auge zu behalten.
 
Status
Nicht offen für weitere Antworten.

Neue Themen


Oben