# ManagedProperty ApplicationScope



## engeliii23 (16. Aug 2012)

Hi zusammen

Habe bis jetzt nur immer als Gast gelesen, jetzt habe ich selbst mal eine Frage.

Ich implementiere gerade eine eigene GameLobby, und jetzt ist mir aufgefallen, dass mir beim Storen der einzelnen GameRooms ein Fehler unterlaufen ist.
Und zwar habe ich als Key einen String verwendet und als Value mein eigener GameRoom.

```
private Map<String, GameRoom> gameRooms;
```

Jetzt habe ich festgestellt, dass, obwohl ich den selben Key aus Copy-Past verwendet habe, beim Beitreten in einen vorhandenen GameRoom einene Fehler gibt, nämlich, dass die GameID nicht übereinstimmt.

Ich habe auch schon eine Vermutung.. Und hoffe deshalb, dass Ihr mir einen einfacheren Weg zeigen könnt. Ich vermute, dass die HashMap den Key mit == überprüft, was zur Folge hat, dass nicht beide String-Objekte gleich sind. Denn wenn ich mir die ganze Map ausgebe und diese dann in einem TestProgramm mit equals vergleiche, dann ist alles zu 100% true. Was ich aber dann komisch finde ist, dass ich in ca. 80% der Tests durch komme, nur in 20% nicht. Wieso ist das so?

Danke schon mal im Voraus für eure Hilfe.


----------



## faetzminator (16. Aug 2012)

engeliii23 hat gesagt.:


> Ich habe auch schon eine Vermutung.. Und hoffe deshalb, dass Ihr mir einen einfacheren Weg zeigen könnt. Ich vermute, dass die HashMap den Key mit == überprüft, was zur Folge hat, dass nicht beide String-Objekte gleich sind. [...]


Falsch, natürlich vergleicht die [japi]HashMap[/japi] mit [c]equals()[/c] (und [c]hashCode()[/c]).


engeliii23 hat gesagt.:


> Denn wenn ich mir die ganze Map ausgebe und diese dann in einem TestProgramm mit equals vergleiche, dann ist alles zu 100% true. Was ich aber dann komisch finde ist, dass ich in ca. 80% der Tests durch komme, nur in 20% nicht. Wieso ist das so?


Weil die Keys nicht übereinstimmen. Lass die mal zwischen "" ausgeben, dann siehst du auch "versteckte" Leerzeichen etc.


----------



## Michael... (16. Aug 2012)

engeliii23 hat gesagt.:


> Ich habe auch schon eine Vermutung.. Und hoffe deshalb, dass Ihr mir einen einfacheren Weg zeigen könnt. Ich vermute, dass die HashMap den Key mit == überprüft


Falsch vermutet: Neben dem Hash der Keys wird auf == || equals geprüft.
Ich vermute der Fehler liegt an anderer Stelle.


----------



## engeliii23 (16. Aug 2012)

faetzminator hat gesagt.:


> Lass die mal zwischen "" ausgeben, dann siehst du auch "versteckte" Leerzeichen etc.



Wieso sollte ich dann versteckte leerzeichen sehen?
Achso, Tschuldigung, ich habe vergessen zu sagen, dass ich natürlich, bevor ich die Keys store, immer trimme, um eben genau die Leerzeichen etc. zu verhindern. Die Keys sind gleich, sie sind vom System generiert, der einzige unterschied ist, dass der zweite über Copy-Paste eingesetzt wird, aber auch dann funktionierts (meistens).


----------



## engeliii23 (16. Aug 2012)

Michael... hat gesagt.:


> Falsch vermutet: Neben dem Hash der Keys wird auf == || equals geprüft.
> Ich vermute der Fehler liegt an anderer Stelle.



Okay, aber wo..? :S Ich das  Ganze geschieht via Webpage (JSF 2.0), und die Strings sind ja gleich, deshalb kapiere ich das ja nicht mehr.. ich versuche jett sicher schon 2 Tage daran...


----------



## faetzminator (16. Aug 2012)

engeliii23 hat gesagt.:


> [...] Die Keys sind gleich, sie sind vom System generiert, der einzige unterschied ist, *dass der zweite über Copy-Paste eingesetzt wird*, aber auch dann funktionierts (meistens).



Genau darum will ich auf die Leerzeichen hinaus


----------



## engeliii23 (16. Aug 2012)

Hööh...? Kannst du mir das mal genauer erklären..? Trotz trim noch leerzeichen(wtf)? 

Okay also soll ich den token so ausgeben:

```
System.out.print("\"" + token + "\"");
```

Oder was....?? Und dann sollte ich z.t. leerzeichen sehen..?


----------



## faetzminator (16. Aug 2012)

Debugger oder Konsolenausgaben wären eine Idee, gerade wenn man noch die Keys der Map (sortiert) ausgibt. Dann kann man sich mit eigenen Augen davon überzeugen, dass die HashMap nicht lügt


----------



## nillehammer (16. Aug 2012)

Fragen wir mal anders: Welcher Teil Deines Codes (bitte ggf. posten) bringt Dich denn zu der Annahme, dass die HashMap gleiche Keys nicht erkennt?

Falls Du damit die Möglichkeit mehrfacher puts mit selbem Key meinst, ist das *kein* Indiz. Es wird dabei nämlich nur der Value des Mappings ersetzt. Auch der Aufruf von get, der null zurück gibt ist kein valider Beweis, wenn die Map null als Values zulässt.

Der einzig richtige Weg, fest zu stellen, ob ein Key in einer Map bereits vorhanden ist, ist der Aufruf von containsKey(...). Und ich bin ziemlich sicher, dass dieser bei gleichen Keys ein true zurück gibt.


----------



## Spacerat (16. Aug 2012)

Verwendest du etwa IdentityHashMap? Dann wär's kein Wunder, weil die vergleicht ausschliesslich mit "==". Im übrigen trägt diese Map deswegen den Namen "Hash" völlig zu unrecht würd' ich sagen.


----------



## engeliii23 (16. Aug 2012)

Aalso.. Zur allgemeinen Fehlerbeseitigung:

Der Aufruf erfolgt via generiertem Code, welcher dem GameRoom Owner mitgeteilt wird, wenn der Room erstellt ist. Dieser kann er dann den gewünschten Leuten mitteilen, welche dann joinen können, bis der Room voll ist. Dies geschieht über ein gganz normales h:form / inputtext des JSF:

```
<h:outputLabel for="gameRoomId" value="Spiel ID: " />
<h:inputText id="gameRoomId" value="#{playerController.gameRoomId}" required="true" requiredMessage="Angabe ben&ouml;tigt!" />
<h:message for="gameRoomId" />
						
<h:commandLink action="#{playerController.joinGameRoom}" value="Spiel beitreten">
</h:commandLink>
```

Diese wird dann via playerController (SessionScoped) an den GamesController (ApplicationScoped) weitergeleitet, welcher dann den aufruf prüft, und die eingegebene GameID, welche im Player selbst gespeichert wird, prüft, und falls diese schon vorhanden ist, den Player dem Game hinzufügt:


```
public boolean joinGameRoom(Player player) {
		boolean retc = false;
		if (gameRooms.containsKey(player.getGameRoomId())) {
			if (gameRooms.get(player.getGameRoomId()) != null) {
				if (gameRooms.get(player.getGameRoomId()).addPlayer(player)) {
					retc = true;
				}
			}
		}
		return retc;
	}
```

Jetzt seht ihr, dass ich containsKey, etc. alles verwende... Ich finde echt den Fehler nicht...... Und ps: Debugging habe ich auch schon versucht, der Room ist erstellt, alles ist vorhanden, der Key ist OHNE Leerschläge in der Map vorhanden....

Wie gesagt in ca. 20% der Fälle wird die GameID, welche ein simpler Token ist, 25 Zeichen ohne Sonderzeichen, nur a-zA-Z0-9.... immer alle 4 Zeichen mit Bindestrich getrennt...


----------



## faetzminator (17. Aug 2012)

Was gibt denn [c]false[/c] zurück? Zeile 3 oder 4? Kannst du das mal in Erfahrung bringen?
Ansonsten, verwendest du DI (Spring o.ä.)? Vielleicht klappt da was nicht mit der Session und App scoped DI? Oder du säuberst irgendwann die Objekte?
Oder wie auch immer, raten kann man lange...


----------



## engeliii23 (17. Aug 2012)

Aalso..

Ich habe das ganze schon debuggt, die Objekte sind immernoch vorhanden, auch wenn es fehlschlägt. Die Zeile, die false zurückgibt, ist die:

```
if (gameRooms.containsKey(player.getGameRoomId())) {
```
aber wenn ich debugge und nachher von hand schaue, ist das Objekt mitsamt der UserListe des GamRooms vorhanden....

GamesController:

```
@ManagedBean
@ApplicationScoped
public class GamesController {
.....
.....
public boolean joinGameRoom(Player player) {
		boolean retc = false;
		if (gameRooms.containsKey(player.getGameRoomId())) {
			if (gameRooms.get(player.getGameRoomId()) != null) {
				if (gameRooms.get(player.getGameRoomId()).addPlayer(player)) {
					retc = true;
				}
			}
		}
		return retc;
	}
```

PlayerController:

```
@ManagedBean
@SessionScoped
public class PlayerController {

	@ManagedProperty(value="#{gamesController}")
	private GamesController gamesController;
.....
.....
	public void joinGameRoom() {
		if (gamesController.joinGameRoom(player)) {
			pageState+=2;
		} else {
			pageState = 1;
			player.setGameRoomId("");
		}
	}
```

So haben wir eine Verbindung. Und die steht ja, würde sonst ja nicht bis zum containsKey kommen.

Damit der Zusammenhang ganz hier ist noch die Atribute der Klasse Player:

```
public class Player {

	private String id;
	private String name;
	
	private String gameRoomInitType;
	private String gameRoomId;
```

Danach normale Getter und Setter jedes Attrubutes wo nötig, und beim setGameRoomId()--> trim()...


----------



## faetzminator (17. Aug 2012)

Da wir nicht für dich debuggen können, gib mal folgendes aus:

```
if (gameRooms.containsKey(player.getGameRoomId())) {
    // ...
} else {
    System.err.println("not found: '" + player.getGameRoomId() + "'");
    System.err.println("rooms: " + gameRooms.keySet());
}
```


----------



## engeliii23 (17. Aug 2012)

```
not found: 'aNlFT-ez7d-yp6a-grZr-fQUN-uIuy-AHr0-Z8O0-PqU3-On2w'
rooms: []
```

okay, schritt weiter.. beim debuggen wird der room angezeigt... In dem Browser, in dem ich den Room rstellt habe, bleibt der Room auch (d.h. er existiert noch in der Map, sonst könnten die GameInfos nicht dargestellt werden, und die werden immer aus dem GameRoom geholt..). Wenn ich in dem Browser, der Joint, es aber nicht funktioniert hat, den Browsercache lösche ( = neue Session), dann gehts es wieder beim nächsten Versuch.... WTF?
Laggiger Browser..? Oder An den Scopes der Beans was verhauen? (müsste ja eben nicht, aber wieso ist die liste dann leer? Ich "resette" nirgends die liste... das game ist ja DA-.-


```
package ch.savk.gamelobby.web.global;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;

import ch.savk.gamelobby.rto.GameTypesRTO;
import ch.savk.gamelobby.type.GameRoom;
import ch.savk.gamelobby.type.GameType;
import ch.savk.gamelobby.type.Player;
import ch.savk.gamelobby.util.TokenGenerator;

@ManagedBean
@ApplicationScoped
public class GamesController {

	private List<Player> players;
	private Map<String, GameRoom> gameRooms;
	
	
	public GamesController() {
		players = new ArrayList<Player>();
		gameRooms = new HashMap<String, GameRoom>();
	}
	
	public void startGame(Player player) {
		gameRooms.get(player.getGameRoomId()).start();
	}
	
	public boolean joinGameRoom(Player player) {
		boolean retc = false;
		if (gameRooms.containsKey(player.getGameRoomId())) {
			if (gameRooms.get(player.getGameRoomId()) != null) {
				if (gameRooms.get(player.getGameRoomId()).addPlayer(player)) {
					retc = true;
				}
			}
		} else {
			System.err.println("not found: '" + player.getGameRoomId() + "'");
			System.err.println("rooms: " + gameRooms.keySet());
		}
		return retc;
	}
	
	public String addNewGameRoom(Player player) {
		GameRoom gameRoom = new GameRoom();
		String gameRoomToken = TokenGenerator.generateToken();
		gameRooms.put(gameRoomToken, gameRoom);
		return gameRoomToken;
	}
	
	public void registratePlayer(Player player) {
		players.add(player);
	}
	
	public void removePlayer(Player player) {
		players.remove(player);
		try {
			gameRooms.get(player.getGameRoomId()).removePlayer(player);
			if (gameRooms.get(player.getGameRoomId()).getPlayers().size() == 0) {
				gameRooms.remove(player.getGameRoomId());
			}
		} catch (Exception e) {}
	}
	
	public GameRoom getGameRoomById(String gameId) {
		return gameRooms.get(gameId);
	}

	public List<Player> getPlayers() {
		return players;
	}

	public List<GameType> getGameTypes() {
		return GameTypesRTO.getGameTypes();
	}
}
```


----------



## SlaterB (17. Aug 2012)

> @ManagedProperty(value="#{gamesController}")
> private GamesController gamesController;

mir fehlt das Detailwissen, aber ist das hier das richtige oder erstellt es vielleicht ein neues Objekt?
logge im Konstruktor von GamesController, zumindest wie oft es erstellt wird 

> Laggiger Browser..? 

den Browser lasse aus dem Spiel, in erster Linie musst du wissen, was auf Serverseite passiert,
das beginnt mit einem Log aller einkommenden Request (und ungefähre Ahnung/ Wissen/ Log, was das jeweils für Auswirkungen zum Zustand aller Datenstrukturen hat)

wenn der Browser cacht, dann kommt vielleicht kein Request zum Server, sollte nicht schlimm sein


----------



## engeliii23 (17. Aug 2012)

@managedProperty erstellt kein neues Objekt, es assoziiert das vorhandene (Deshalb muss die Session gleich oder länger dauern, da sonst irgendwann null vorkommen würd.e..)... DI... bei angabe eines name="" im managedBean ist Namensänderung des Objektes möglich, ansonsten ist es unter dem Klassennamen zu finden... (einfach lowCamelCase).....
okay, aber weshalb funktioniert dann KEIN Join mit der Broswersession, und sobald eine neue aufgebaut wird, funktioniert es? (Werden dann ja auch neue userSessions serverseitig angelegt).


----------



## SlaterB (17. Aug 2012)

mal zurück auf
> not found: 'aNlFT-ez7d-yp6a-grZr-fQUN-uIuy-AHr0-Z8O0-PqU3-On2w' rooms: [] 

was ist diese Ausgabe? ist das ein Request eines 'joinenden Browsers B'?
diese Ausgabe sagt doch klipp und klar, dass die Map leer ist,
wenn das der Fall ist muss man dem Browser keinen Vorwurf machen, 
mehr als in die Java-Klasse hineinzudringen und die Map abzufragen geht ja kaum

und du sagst,
- dass zu diesem Zeitpunkt ein anderer User A bereits einen Room erstellt hat, einen GamesController mit nicht-leerer Map kennt?
(auch nach der  "rooms: [] " nochmal bestätigt?)
- dass bei irgendeiner Art Reset B dann auch den Raum findet, ohne dass dieser speziell angelegt wird, sondern jetzt gefunden, Map nicht leer?

mysteriöse Sache, ich kann vorerst beitragen:
- logge bitte auch den Hashcode von gamesController bei jeder Ausgabe, etwa
> System.err.println("rooms: " + hashCode()+", "+gameRooms.keySet());

- logge jedes Erstellen eines GamesController, gib dort gleich seinen hashCode() aus, damit man weiß welcher das ist,
logger mit new Error().printStackTrace(); die Aufrufer, vielleicht kann man ungefähr erkennen, wer das jeweils macht,
günstig ist natürlich genau zu verfolgen, welcher User gerade welchen Request macht,
auch im Log bzw. du weißt ja sicherlich was du gerade aufgerufen hast


----------



## engeliii23 (17. Aug 2012)

>INFO: Server startup in 3904 ms
>GamesController: 1336627115
>GamesController: 1281510498
>not found: '9QSGw-DrYq-bq3R-7eOn-RBsd-rbWH-xG84-qQi4-9ywg-rw0H'
>rooms: []
>GamesController: 1336627115
>found
----------
Okay, du hast recht. Es wird ein neues Ojekt erzeugt, warum auch immer.... Jemand eine Idee.?

Ich probiers einfach mal mit @ManagedBean (name="<name>")


----------



## Fant (17. Aug 2012)

SlaterB hat gesagt.:


> > @ManagedProperty(value="#{gamesController}")
> > private GamesController gamesController;
> 
> mir fehlt das Detailwissen, aber ist das hier das richtige oder erstellt es vielleicht ein neues Objekt?
> logge im Konstruktor von GamesController, zumindest wie oft es erstellt wird



Nein, mit der @ManagedProperty-Annotation nicht. Hier wird wirklich auf die ManagedBean aus dem entsprechenden Scope zurückgegriffen. Anders wäre das mit den @Inject-Annotation. Damit würde wirklich eine neue Instanz der Bean angelegt werden.
Um ganz sicher zu sein könnte man sich die Instanz des GameController aber testweise auch mal im Konstruktor manuell aus dem FacesContext holen.

Den Fehler hab ich aber auch noch nicht entdecken können.

Edit:
siehePost über mir: Jedenfalls _sollte_ das so sein


----------



## Fant (17. Aug 2012)

Noch ne Idee (keine Ahnung, ob es die bemerkten Auswirkungen haben kann)
Gibt es im PlayerController getter und setter für den GameController?

Edit:
Achja, manuell bekommst du den GameController in etwa so:

```
public PlayerController() {
 ...
 FacesContext fc = FacesContext.getCurrentInstance();
 gameController = (GameController) fc.getApplication().getELResolver().getValue(fc.getELContext(), null, "gameController");
 ...
}
```


----------



## engeliii23 (17. Aug 2012)

Fant hat gesagt.:


> Den Fehler hab ich aber auch noch nicht entdecken können.


Ich bin jetzt seit einem Jahr ca. auf JSF, aber das hab ich jetzt wirklich auch noch nie gehabt....>.< 
Funktioniert übrigens auch mit igenem name-Attribut nicht....


----------



## engeliii23 (17. Aug 2012)

Fant hat gesagt.:


> Noch ne Idee (keine Ahnung, ob es die bemerkten Auswirkungen haben kann)
> Gibt es im PlayerController getter und setter für den GameController?



Ja, die müssen vorhanden sein, denn ohne diese Funktioniert ManagedProperty gar nicht und das Attribut kann nicht erstellt werden. JSF halt..^^

Andere Frage:
Funktioniert das überhaupt so, denn rein theoretisch gibt es ja keinen GamesController beim ersten Zugriff, also kann ich diesen auch nicht rausholen.... Und, wieso erstellt er einen neuen, wenn eigentlich schon einer vorhanden ist? ..>.< ich schliesse mal Fehler aus und cleane den Workspace, neue saubere ServerKonfukuration, etc, bringt das ev. was..? (Ist aber eig neu.... 2 wochen alt)


----------



## SlaterB (17. Aug 2012)

ich kann mir keine Variante denken, die erklärbar wäre, dennoch wären mehr Infos schön, etwa die Reihefolge:
sei A der User der den Raum erstellt und B der ihn nicht sieht,

kommt B als erster dran, danach A? -> dann könnte man sich vorstellen, das jeder neuer User, warum auch immer, den bisherigen GameController überschreibt, die alten User bei Reset den neueren übernehmen,

interessanter Test dazu: wenn A den Raum erstellt, B ihn irgendwann auch sieht, erstelle einen dritten User C
-> kommt noch ein neuer GameController, ohne Raum? würden A und B bei Reset ihrerseits, oder gar schon bei normalen neuen Requests, auch den Raum 'verlieren', den dritten leeren GameController sehen?

usw. an Konstellationen, wie gesagt nicht erwarten dass zumindest mir dadurch ein Licht aufgeht, aber untersuchungswert sind alle Kombinationen

alles vornehmlich mit Logs im Server, was Browser vielleicht aus dem Cache anzeigen spielt keine Rolle


----------



## engeliii23 (17. Aug 2012)

Also.. Ich habe jetzt mal ein wenig gestetet, und ein paar Browserinstanzen geöffnet^^

Folgendes Szenario währe eigentlich richtig:
A gibt einen namen ein, erstellt ein neues Spiel und wählt den Spieltyp (Tictactoe, Monopoly, etc., jenachdem gibt es eine andere Anzahl möglicher Spieler). Danach sieht er auf einer Übersicht die RoomID, welche 50 stellig ist, zur Sicherstellung, dass der Room nur einmal existiert.
A kann jetzt die ID an andere Spieler weitergeben an Spieler B, C, D, etc., via Chat z.b., und dann können diese ebenfall einen Namen wählen, und dann anstelle von neues Spiel erstellen wählen diese dann Spiel beitreten, geben die ID ein und, im main-Scenario jedenfalls xD, treten diese dann solange noch Platz ist, dem Raum bei.

In meinem Szenario hat A den Raum erstellt, und  B hat versucht beizutreten, was fehlgeschlagen ist. C hat dann ebenfalls versucht den Raum zu betreten, was dann widerum erfolgreich war..<.> :applaus:
Erklärung haben wir ja eigentlich schon, es sind nicht die gleichen Objekte.... A und C haben jedoch laut Konsolenoutput auf das gleiche zugegriffen, nur B hatte ein anderes.

In einem weiteren Versuch habe ich mir noch ein D dazugenommen, dieses Mal hatte keiner den gleichen hashCode wie A, aber B hatte den gleichen wie C und D.



Kann es sein dass ich im web.xml etwas deklarieren muss..? Oder in der Serverkonfiguration selbst..? Ich teste im Moment alles in Eclipse selbst, mit dem Tomcat 7.0.29...


----------



## engeliii23 (17. Aug 2012)

SlaterB hat gesagt.:


> -> kommt noch ein neuer GameController, ohne Raum?


Ja, sporadisch bei neuen Users...



SlaterB hat gesagt.:


> würden A und B bei Reset ihrerseits, oder gar schon bei normalen neuen Requests, auch den Raum 'verlieren', den dritten leeren GameController sehen?


Nein, leider nicht, das wäre von mir aus auch schon okay.. :bahnhof:
Die Users behalten ihre Sessions dies sie mal bekommen haben, eben einfach ausser Cache-reset, aber das wäre ja auch eine ganz neue Session.. Ich kann mit den bestehenden Usern machen was ich will, sie bleiben bei ihrer session


----------

