# ResultSet is closed tritt bei JSF aber nicht bei einer Java-Applikation auf



## Holy Diver (16. Apr 2012)

Bin mir nicht ganz sicher ob das jetzt der richtige Bereich ist da der Fehler nur bei einer JSF-Anwendung auftritt.
Jedes Mal wenn ich die Methode getSpecialSettings in einer JSF-Anwendung aufrufe führt dies zu
der SQL-Exception: Operation not allowed after ResultSet closed.

Führe ich folgende Java-Applikation aus kommt es aber zu keinem Fehler:

```
public static void main(String[] args) {
		LinkedList<Integer> n=new LinkedList<Integer>();n.add(798);
		LinkedList<specialsetting> s=DAO.getInstance().getSpecialSettings(n);
		s=DAO.getInstance().getSpecialSettings(n);
		DAO.getInstance().getClasses();
		s=DAO.getInstance().getSpecialSettings(n);
	}
```
getSpecialSettings:

```
public LinkedList<specialsetting> getSpecialSettings(LinkedList<Integer> IDs) {
		
             ........
		// Get a Statement:
		Statement stmt = pool.checkOut();
		
		try {
			ResultSet res = stmt.executeQuery(Query);
			....
			int ID;
			String sd_name,desc;
			while(res.next()){//Nach einen Durchlauf tritt hier der Fehler auf
				ID=res.getInt("ID");
				sd_name=res.getString("sd_name");
				desc=res.getString("description");
				
				........

				System.out.println("res "+res.isClosed());
				System.out.println("stmt " +stmt.isClosed());
			}//while
			.....	
		} catch (SQLException e) {
			e.printStackTrace();
		}
		// Return the Statement:
	    pool.checkIn(stmt);
		return specialsettings;
	}
```
Ausgabe:res true
stmt false
java.sql.SQLException: Operation not allowed after ResultSet closed

Der Pool gibt meinem Erachten kein Statement zweimal zurück, wenn er dies machen würde, gäbe es ja auch bei der Java-Applikation einen Fehler.
Ich benutze mysql-connector-java-5.1.18 und einen Tomcat v6.

Würde mich über Lösungsvorschläge oder über Vermutungen zur Ursache freuen.


----------



## SlaterB (16. Apr 2012)

> Der Pool gibt meinem Erachten kein Statement zweimal zurück

Tipp: erachte nicht sonder prüfe und wisse dann,
gibt von den ResultSets den HashCode aus, oder speichere sie temporär in Variablen und vergleiche sie mit ==,
oder wrappe alle Statements in eigenen Klassen und gib ihnen eindeutige Nummern im Konstruktor

ohne den Pool ist jedenfalls keine gute Aussage zu treffen,
wird dort jemals close() ausgeführt? wenn in deinem Code, dann logge auch das, wieder mit Hashcode/ == / Nummer

checkOut/ checkIn soll gleichzeitige Verwendung vermeiden?

logge ob bei JSF die Aufrufe von verschiedenen Threads gleichzeitig kommen
(Beginn, Ende loggen, Thread.getCurrentThread() für den Ausführer usw., 
die Parameter-Ids sind auch sicher gleich und haben keinen Einfluss wie Exception bei der Query oder so?)


----------



## maki (16. Apr 2012)

Deine Statement Pool Implementierung ist wohl fehlerhaft bzw. vom Grundkonzept falsch, IMHO poolt man keine Statements sondern Connections, jeder moderne JDBC Treiber bringt einen ConnectionPool mit.


----------



## Holy Diver (17. Apr 2012)

SlaterB hat gesagt.:


> > Der Pool gibt meinem Erachten kein Statement zweimal zurück
> 
> Tipp: erachte nicht sonder prüfe und wisse dann,
> 
> ...


Sollten eigentlich schon dürfte aber nicht ganz funktionieren.
res.close() wird nirgends aufgerufen(wurde alles auskommentiert) 
Bis zu folgenden Punkt wird immer ein checkOut und als nächstes ein CheckIN(es war immer nur ein Statement im Umlauf)

Ausgabe: 
SpecialSettingsBean created!
Thread[http-8080-2,5,main] checkOut 1
Thread[http-8080-2,5,main] checkOut 1
Thread[http-8080-2,5,main] checkIn 1
res true
exstmt.stmt false 
java.sql.SQLException: Operation not allowed after ResultSet closed



SlaterB hat gesagt.:


> >
> die Parameter-Ids sind auch sicher gleich und haben keinen Einfluss wie Exception bei der Query oder so?)


Ich habe mir den Parameter bei getSpecialSettings ausgegeben lassen und ihn bei der Java-Applikation verwendet, also diese sind sicher gleich.

```
public synchronized ExtendedStatement checkOut() {
		Statement stmt;
		ExtendedStatement exstmt;
		if (unlocked.size() > 0) {
			for(int i=0;i<unlocked.size();i++){
				stmt=unlocked.get(i).stmt;
				exstmt=unlocked.get(i);
					if (validate(stmt)) {
						unlocked.remove(stmt);
						locked.add(exstmt);
						System.out.println("checkOut"+exstmt.ID);
						return (exstmt);
					} else {
						// object failed validation
						unlocked.remove(exstmt);
						expire(stmt);
						i--;
						stmt = null;
						exstmt=null;
					}
			}
		}
		exstmt = create();
		locked.add(exstmt);
		System.out.println("checkOut"+exstmt.ID);
		return (exstmt);
	}
public synchronized void checkIn(ExtendedStatement stmt) {
		locked.remove(stmt);
		//unlocked.put(stmt, System.currentTimeMillis());
		System.out.println("checkIn"+stmt.ID);
		unlocked.add(stmt);
	}

 protected ExtendedStatement create() {
	    try {
	    	if(conn.isClosed()){
	    		NewConn();//Erzeugen der Connection
	    		}
	    	nrofstmt++;
	    	ExtendedStatement exstmt=new ExtendedStatement(conn.createStatement(),nrofstmt);
	    	return (exstmt);
	    } catch (SQLException e) {
	      e.printStackTrace();
	      return (null);
	    }
	  }
```



maki hat gesagt.:


> Deine Statement Pool Implementierung ist wohl fehlerhaft bzw. vom Grundkonzept falsch, IMHO poolt man keine Statements sondern Connections, jeder moderne JDBC Treiber bringt einen ConnectionPool mit.


Der Pool soll verhindern, dass die Statments immer neu erzeugt werden müssen und soll somit die Performance verbessern. Der Pool ist allerdings eine Vorgabe für mich.


----------



## maki (17. Apr 2012)

> Der Pool soll verhindern, dass die Statments immer neu erzeugt werden müssen und soll somit die Performance verbessern. Der Pool ist allerdings eine Vorgabe für mich.


Hört sich ehrlich gesagt sehr wirr an...
Gibt es Beweise dafür dass es dann schneller geht? 

Ein ResultSet gehört zu einem Statement und dieses wiederrum zu einer Connection.
Connection wird geschlossen -> Statement wird geschlossen -> ResultSet wird geschlossen

Wie gesagt, halte das Poolen von Statements wie hier für grundsätzlich falsch.


----------



## SlaterB (17. Apr 2012)

oha, da habe ich in meinen Posts ja einiges durcheinandergebracht,
im Pool sind die Statements, und die sind ja (hier) nie geschlossen, habe dein Ausgabe-Log falsch interpretiert,
dann muss dazu ja auch nicht wirklich was geprüft werden wie HashCode-Vergleich, 
obwohl trotzdem durchaus interessant sein kann, ob ein Statement wiederverwendet wird

eigentlich wollte ich dazu ja fragen ob die Statements geschlossen werden, an die ResultSets hatte ich gar nicht gedacht,
auch als Text verwechselt in meinem Posting

jetzt schreibst du
> res.close() wird nirgends aufgerufen(wurde alles auskommentiert) 
das ist ja auch nicht so lustig, ResultSets sollten schon geschlossen werden, am Ende der Methode,

ist das ganze eigentlich reproduzierbar oder zufällig?
jetzt aufs ResultSet konzentriert: logge von Anfang an, von der Query, bis in die Schleife in jedem Schleifendurchlauf 
und zu deiner schon vorhandenen Ausgabe, wann ist das ResultSet offen, wann erstmals geschlossen? 
direkt nach executeQuery() doch sicher noch offen oder auch schon zu?
danach zu einem festen reproduzierbaren Zeitpunkt (wann?) oder wechselnd?

gerade wechselnd würde für einen anderen Thread sprechen, denn die Wiederverwendung des Statements,
eine neue Query mit neuen ResultSet, schließt das erste ziemlich sicher, wenn ich mich nicht komplett irre

das Log
> SpecialSettingsBean created!
> Thread[http-8080-2,5,main] checkOut 1
> Thread[http-8080-2,5,main] checkOut 1
> Thread[http-8080-2,5,main] checkIn 1

spricht ja auch dafür, dass das Statement 1 zweimal gleichzeitig herausgegeben wird, oder?
das wäre dann ja der Fehler, obwohl es nicht nach verschiedenen Threads aussieht,
kann es sein dass ein Thread verschachtelt mehrmals Statements anfordert?
widerspricht wiederum der funktionierenden main-Variante..

lieber doch mehrere Threads vermuten, merke dir diese auch irgendwo, z.B. bei jedem checkOut in einer Liste,
dazu ==-Vergleich mit den vorhandenen, ausgeben

aber letztlich kann das auch egal sein, zweimal 
> Thread[http-8080-2,5,main] checkOut 1
> Thread[http-8080-2,5,main] checkOut 1
ist doch in jedem Fall ein Fehler, oder?

ich kann in dem Code zum Pool vorerst nicht so viel erkennen


----------



## Holy Diver (17. Apr 2012)

maki hat gesagt.:


> Gibt es Beweise dafür dass es dann schneller geht?


Ich kenne keine,allerdings habe ich mich bis jetzt eher wenig mit Performance beschäftigt


maki hat gesagt.:


> Ein ResultSet gehört zu einem Statement und dieses wiederrum zu einer Connection.
> Connection wird geschlossen -> Statement wird geschlossen -> ResultSet wird geschlossen


Das kann hier allerdings nicht der Grund für den Fehler sein,da das Statement zum Zeitpunkt des Fehlers noch offen ist.




SlaterB hat gesagt.:


> jetzt schreibst du
> > res.close() wird nirgends aufgerufen(wurde alles auskommentiert)
> das ist ja auch nicht so lustig, ResultSets sollten schon geschlossen werden, am Ende der Methode,


Grundsätlich passiert dass bei mir immer vor dem zurückgeben,allerdings wurde dies im Laufe der Fehlersuche auskommentiert.


SlaterB hat gesagt.:


> ist das ganze eigentlich reproduzierbar oder zufällig?


Der Fehler tritt immer auf,wenn ich die Methode mit einer ID,welche in der DB vorhanden ist,aufrufe.





SlaterB hat gesagt.:


> jetzt aufs ResultSet konzentriert: logge von Anfang an, von der Query, bis in die Schleife in jedem Schleifendurchlauf
> und zu deiner schon vorhandenen Ausgabe, wann ist das ResultSet offen, wann erstmals geschlossen?
> direkt nach executeQuery() doch sicher noch offen oder auch schon zu?


Direkt nach dem execute müsste es noch offen sein,sonst würde die Schleife nicht ausgeführt.


SlaterB hat gesagt.:


> danach zu einem festen reproduzierbaren Zeitpunkt (wann?) oder wechselnd?
> 
> gerade wechselnd würde für einen anderen Thread sprechen, denn die Wiederverwendung des Statements,
> eine neue Query mit neuen ResultSet, schließt das erste ziemlich sicher, wenn ich mich nicht komplett irre


Hier irrst du dich sicher nicht,denn Fehler hatte ich vor nicht ganz so langer Zeit. 


SlaterB hat gesagt.:


> das Log
> > SpecialSettingsBean created!
> > Thread[http-8080-2,5,main] checkOut 1
> > Thread[http-8080-2,5,main] checkOut 1
> ...


Schaut für mich auch danach aus,dass ein Statment zweimal zurückgegeben wird.
Allerdings sollte das mit den Listen und dem synchronized verhindert werden.

Es gibt Methoden die mehr als ein Statment  benutzen oder eine andere Methode aufrufen(die wiederum ein Statment) benutzen,während diese noch ein stmt aus dem Pool haben.


SlaterB hat gesagt.:


> lieber doch mehrere Threads vermuten, merke dir diese auch irgendwo, z.B. bei jedem checkOut in einer Liste,
> dazu ==-Vergleich mit den vorhandenen, ausgeben


Ich habe jetzt bei jedem System.out.printl("checkOut"...) ein threads.add(Thread.currentThread().hashCode());.
//LinkedList<Integer> threads=new LinkedList<Integer>();
Beim CheckIn threads.removeFirstOccurrence(Thread.currentThread().hashCode()); und am Anfang von CheckOut:if(threads.contains((Thread.currentThread().hashCode()))){
			System.out.println("Thread gets multiple stmt");}
Hoffe das war so gemeint.



SlaterB hat gesagt.:


> aber letztlich kann das auch egal sein, zweimal
> > Thread[http-8080-2,5,main] checkOut 1
> > Thread[http-8080-2,5,main] checkOut 1
> ist doch in jedem Fall ein Fehler, oder?


Wenn es zweimal das gleiche Statment ist,ist es sicher ein Fehler.

Log:
SpecialSettingsBean created!
Thread[http-8080-1,5,main] checkOut 1
res1 false
res2 false
res3 false
res3.0.1 false
Thread gets multiple stmt
Thread[http-8080-1,5,main] checkOut 1
Thread[http-8080-1,5,main] checkIn 1
res3.1 true
res4 true
exstmt.stmt false
java.sql.SQLException: Operation not allowed after ResultSet closed

Der Fehler titt also zwischen 3.01 und 3.1 auf

```
....
System.out.println("res3 "+res.isClosed());
				if(specialsettings.isEmpty() && specials==null){
					specials=new specialsetting();
                                        //Wertzuweisungen
					specials.ID=ID;
					.......
                                        System.out.println("res3.0.1 "+res.isClosed());
					specials.extendRoomlist(upis, getRoomList(re_ID,upis));
					System.out.println("res3.1 "+res.isClosed());
				}
...
private LinkedList<Room> getRoomList(int re_ID,String upis)
	{
		String Query="..."
		// Get a Statement:
		ExtendedStatement exstmt = pool.checkOut();
		LinkedList<Room> roomlist = new LinkedList<Room>();
		
		try {
			ResultSet res=exstmt.stmt.executeQuery(Query);
			while(res.next()){
				roomlist.add(new Room(res.getString("room_ID"),res.getInt("size")));
			}
			//res.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		
	    pool.checkIn(exstmt);
	    return roomlist;
	}
```
Der Fehler dürfte wirklich darin liegen, dass getRoomList(..) ein bereits verwendetes Statemant bekommt. Ich kann aber nicht nachvolzihen warum der Pool ein Statemant zweimal vergibt.


----------



## SlaterB (18. Apr 2012)

schöne Debug-Log-Ausgaben, sieht man seltener als ich sie fordere 
warum machst du damit im Pool nicht weiter?
habe ihn mir jetzt erst angeschaut, soviel du davon gepostet hast, 
du läßt ja gar manche Variablen wie unlocked/ locked im Dunkeln, auch wenn es wohl Listen sind,

der Code ist aber überschaubar mit nur wenigen Schleifen, add/ remove, das kann man doch im Detail anschauen,
wie groß ist die Liste, welche Elemente werden geprüft, warum gelten sie jeweils als ok oder nicht ok usw.,

der aktuelle Fehler scheint
>  unlocked.remove(stmt);
zu sein, in der Liste ist doch stmt gar nicht drin sondern exstmt,
das exstmt bleibt so in unlocked und kann mehrfach zurückgegeben werden,

falls du diese Wrapper-Klasse erst auf meinen Vorschlag hin gebaut hast, bin ich wohl daran schuld und es mag der Original-Fehler immer noch irgendwo schlummern,
so wie es aktuell steht dürfte die main-Methode auch scheitern, oder?
denn dort wird ja genauso die Untermethode mit zweitem checkOut usw. gerufen


----------



## Holy Diver (19. Apr 2012)

SlaterB hat gesagt.:


> warum gelten sie jeweils als ok oder nicht ok usw.,


Die Methoden hatte ich nicht gepostet, da sie ja nicht der Grund für eine doppelte Ausgabe sein kann.
[Java] 
private  LinkedList<ExtendedStatement> locked;
private LinkedList<ExtendedStatement> unlocked;
.....
 public void expire(Statement stmt) {
	    try {
	      stmt.close();
	    } catch (SQLException e) {
	      e.printStackTrace();
	    }
	  }
 public boolean validate(Statement stmt) {
	    try {
	      return (!stmt.isClosed());
	    } catch (SQLException e) {
	      e.printStackTrace();
	      return (false);
	    }
	  }[/Java]


SlaterB hat gesagt.:


> der aktuelle Fehler scheint
> >  unlocked.remove(stmt);
> zu sein, in der Liste ist doch stmt gar nicht drin sondern exstmt,
> das exstmt bleibt so in unlocked und kann mehrfach zurückgegeben werden,
> ...


Ja die main funktionierte auch nicht mehr. 
Die Wrapper-Klasse entstand erst auf den Vorschlag hin,aber wenn ich nun unlocked.remove() mit exstmt aufrufe tritt das ResultSet nicht mehr auf, obwohl das ja nicht der Grund für den vorherigen Fehler sein kann und auch wenn ich die ExtendedStatements mit normalen Statements austausche geht es auch.

Die einzige Änderung die mir einfällt wäre,dass ich bei 

```
} else {
		// object failed validation
		unlocked.remove(stmt);
		expire(stmt);
		i--;
		stmt = null;
	}
```
das i--; hinzugefügt hatte. Dies würde aber nur dazu führen, dass ein Statement übersprungen würde und damit eines erzeugt wird obwohl sich ein anderes noch in Unlocked befindet.


----------



## SlaterB (19. Apr 2012)

lese ich da heraus, dass das Problem erledigt ist?


----------



## Holy Diver (19. Apr 2012)

SlaterB hat gesagt.:


> lese ich da heraus, dass das Problem erledigt ist?


Ja nur weiß ich nicht was der Fehler war,aber ich glaube das ist ohne den ursprünglichen Pool nicht möglich.


----------

