JSF ManagedBean vertauscht Clients

OliverZ

Mitglied
Hallo zusammen,

bisher dachte ich immer, dass eine Anwendung durch ManagedBeans immer genau das selbe Bean dem selben Client zuordnet. Tut es aber nicht immer, wie ich soeben herausgefunden habe.
Ich habe eine JSF-Anwendung, die anhand von Formularfeldern eine Berechnung durchführt und das Resultat dem Client anzeigt. Die Klassen sind ManagedBean und SessionScoped annotiert.

Nun habe ich mit vier Rechnern gleichzeitig so schnell gerechnet, wie möglich. Als Test, um zu sehen, ob die Applikation auch mehreren parallelen Anwendungen standhält.
Resultat war, dass zum Teil dem einen Client das Resultat der Berechnung eines anderen Clients angezeigt wurde.

Hat jemand eine Idee an was dies liegen kann?
 

OliverZ

Mitglied
Wie ist das eigentlich?
Klasse A ist ein ManagedBean, SessionScoped und instanziert die normale Klasse B. B instanziert wiederum die normale Klasse C. Jetzt eröffnen 4 Clients eine Session an Klasse A. Existieren dann je 4 der Objekte B und C?
Und wenn nicht, wie würde man es erreichen, dass es je 4 Instanzen von B und C gibt?

Meine Anwendung ist so aufgebaut, dass Klasse A Zahlen aus einem Formular an B und folglich an C weitergibt, worin sie dann verrechnet und in eine Liste gesetzt werden. Diese Liste wird danach über Klasse B an A weitergegeben, wo sie in einer Liste auf der XHTML-Seite angezeigt werden. Mein Problem ist nun, dass in einzelnen Fällen Clients die Liste von anderen Clients zurückerhalten, anstatt die eigene.

Vielen Dank für jegliche Hilfe. Möchte dringend diese Anwendung freigeben.
 
S

Sym

Gast
Wie instanziierst Du denn B und C? Sind das auch Beans?

Ich kann mir kaum vorstellen, dass ein anderer Rechner die falsche Bean erwischt. Ich wüsste nicht, wie das gehen sollte.

Ich vermute, Du verwendest eine gemeinsame Bean, auf die die Sessionbeans zugreifen.
 

OliverZ

Mitglied
In Klasse A (ManagedBean, SessionScoped) mache ich ein:
B b = new B();

und in B:
C c = new C();

B und C sind ganz normale Klassen, also ohne weitere Annotation.
B dient als Steuerung der Applikation, C als Speicher für Variablen, die mittels Getter/Setter von A über B nach C geschrieben/gelesen werden.

In B gibt es eine ArrayList, die Objekte speichert. Diese Objekte enthalten Resultate aus weiteren Objekten, die auch in B instanziert werden. Aus B heraus wird auch die Berechnungsmethode, dieser Objekte aufgerufen(und als Parameter wird die Klasse C mitgegeben). Die Berechnungsmethoden instanzieren je ein neues Objekt einer spezifischen Berechnung. Zum Schluss wird jedes Objekt der spezifischen Berechnung mit b.ArrayList.add(objekt) in die ArrayList von B gespeichert.

Die Webseite (XHTML) löst diese ganze Operation aus und navigiert dann auch eine Unterseite, die eine DataTable enthält. Diese DataTable holt sich mittels "value" und "var" die ArrayListe aus A (a.getArrayList). Dieser Getter enthält "return b.getArrayList();". In B enthält getArrayList() lediglich ein "return ArrayList".

Für jeden Client muss eine ArrayList mit individuellen Werten existieren. Wenn man nun mit mehreren Clients schnell hintereinander Rechnungen auslöst (1-2 pro Sekunde) kommt es vor, dass bei einem Client auf einmal die Werte der ArrayList eines anderen Clients in seine DataTable anzeigt. Meistens werden schon die richtigen Werte angezeigt, aber eben nicht immer. Es kommt vorwiegend bei parallelen Berechnungen vor.

Kann es sein, dass in der Zeit die zwischen Abfüllen der ArrayList und nachladen der Seite (die die Arraylist ausliest und in die DataTable schreibt) eine andere Berechnung eines anderen Clients die selbe ArrayList neu abfüllt und so der erste Client die ArrayList des zweiten Clients erwischt?

Müsste man alle weiteren Klassen, die von A instanziert werden, speziell annotieren?

Ich kenne JSF noch nicht sehr lange. Würde es für solch eine Anforderung eine bessere Architektur geben?
 

JimPanse

Bekanntes Mitglied
Hi,

das ganze ist kein JSF Problem sondern die HttpSession:
1. Ist nicht Thread-save!
"Multiple servlets executing request threads may have active access to a single
session object at the same time. The Developer has the responsibility for
synchronizing access to session resources as appropriate."

2. Für Daten, die von mehreren Clients gleichzeitig geändert werden können, auch nicht geeignet! oder du synchronisierst den Zugriff,

s.h.

-> Java(TM) Servlet Specification 2.4 Final Release
s.h. SRV.7.2 Creating a Session

Wenn dann entweder ViewScope oder RequestScope.

Grüße
 

OliverZ

Mitglied
Hallo JimPanse!

Das verstehe ich jetzt nicht ganz. Du meinst, die HttpSession ist nicht Thread-Save?
Dann hat SessionScope gar keinen Einfluss auf das Servlet?

Wenn ich den ganzen Vorgang von dem ManagedBean A auf die weiteren Objekte synchronisiere, dann sollte dies das Problem lösen?

Was genau ist nicht geeignet: "2. Für Daten, die von mehreren Clients gleichzeitig geändert werden können, auch nicht geeignet!"?

Vielen Dank und Gruss
 

OliverZ

Mitglied
OK, ich habe noch etwas nachgelesen.
Wenn ich JSF verwenden, dann wird ja automatisch das Faces Servlet javax.faces.webapp.FacesServlet erstellt.
Möchte ich nun die Funktionen nun Synchronized machen, muss ich dafür ein neues Servlet anlegen oder ist es besser dem Faces Servlet als Value hinzuzufügen?
 

JimPanse

Bekanntes Mitglied
Hi,

überlege dir nochmal genau deinen Ansatz:

In Klasse A (ManagedBean, SessionScoped) mache ich ein:
B b = new B();

und in B:
C c = new C();

B und C sind ganz normale Klassen, also ohne weitere Annotation.
B dient als Steuerung der Applikation, C als Speicher für Variablen,


Grüße
 

OliverZ

Mitglied
Das tue ich seit längerem, aber ich komme nicht auf das Problem.
C beinhaltet je Session andere Werte für die Variablen.
Ist es denn nicht so, dass für jede Session eine eigene Instanz des Objektes C existiert?

Wenn nicht, wie würde man dies denn richtig machen?
Alles in Klasse A machen? Jeder Klasse die Annotation SessionScoped hinzufügen?
Oder muss bei jeder übergabe von Variablen die Session mitgegeben, bzw. abgefragt werden?

Soweit ich über JSF gelesen habe, nahm ich an, dass es diese Probleme selber lösen würde. Ansonsten würde ja die automatische Erzeugung von Sessions gar keinen Sinn ergeben?

Liegt jetzt mein Problem mehr darin, dass an der Architektur etwas konzeptionell falsch ist, oder liegt es eher daran, dass das Servlet nicht Thread-Save ist?
 
Zuletzt bearbeitet:

JimPanse

Bekanntes Mitglied
Hi,

erzähl nochmal deine Versuchsaufbau:

4 unterschiedliche Rechner oder 4 unterschiedliche Browserfenster oder 4 unterschiedliche Tabs eines Browsers???

und die greifen alle auf eine Web-App zu die auf einem Rechner liegt?
 

OliverZ

Mitglied
Es sind 4 Rechner meines Heimnetzwerkes. Die App läuft in einem Glassfish 3.1.1 auf einem Vserver im Netz.
Jeder Rechner hat die App in je einem Tab geöffnet. Für jede Session wurde im Cookie des jeweiligen Rechners eine eindeutige JSESSION-ID erzeugt. Die Eingaben aus dem Formular, das bei jeder Anfrage des Versuches an den Server übermittelt wird, werden mittels Setter-Funktion im ManagedBean per JPA in eine DB gespeichert. In der DB stimmen SessionIds mit den Eingaben der Formularfelder überein.

Das ganze habe ich noch weiter getestet. Es tritt auch schon bei zwei Rechnern auf. Der eine schickt in hoher Frequenz Anfragen, der andere nur alle 5 Sekunden. Nach kurzer Zeit holt sich die DataTable des langsameren die Daten aus der ArrayList des schnelleren Clients.

Was ich noch nicht getestet habe, ist, ob nur die falsche ArrayListe zurückgegeben wird oder ob sie sogar mit den Zahlen einer anderen Session gefüllt wird. Dies werde ich aber demnächst tun.

Danke und Gruss
 

OliverZ

Mitglied
Habe das Problem endlich gelöst. Es hatte nichts mit Threads, Servlets oder Session zu tun.
Die ArrayList, die die berechneten Resultate der Clients gespeichert hat, hatte ich ursprünglich (als es noch eine Java-Desktop-App war) als STATIC deklariert, damit ich sie von überall her füllen konnte. Nun bildet das ManagedBean für jeden Client eine eigene Instanz mit allen darin instanzierten Objekten. Nur was static deklariert wird, wird nicht instanziert. So gab es für die ganze App nur eine ArrayList, die sich die Clients teilten. So kam es, dass die Clients sich die Werte in der Liste gegenseitig überschrieben und je nach Timing die Werte eines anderen Clients beinhalteten. Ist vermutlich ein Anfängerfehler, dessen Problematik ich mir nicht bewusst war.
 

Ähnliche Java Themen

Neue Themen


Oben