Das Notes Forum

Domino 9 und frühere Versionen => ND6: Entwicklung => Thema gestartet von: birdy am 22.04.05 - 08:25:55

Titel: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 08:25:55
Hallo,

mein Java-Agent verursacht immer wieder nen OutOfMemoryError, wenn er mehrmals hintereinander läuft.

Der Agent wird aus einer Notes-DB gestartet, stellt über JDBC eine Connection zur DB2 her und liest dort Daten aus. In der Notes-DB wird dann ein Dokument erstellt und die Daten dort eingelesen. Das Ganze funktioniert im Grunde, hatte mich aber wohl zu früh gefreut.

Nach ca. 7 Aufrufen des Agenten kommt es zu einem Error in Notes:

NotesError: JVM: Versuch, den Java Agent-Anhang zu öffnen, ist fehlgeschlagen.

In der Java-Konsole steht folgender Error:

java.lang.OutOfMemoryError

Kenne mich mit Java leider gar nicht gut aus und bin schon froh, dass ich den Agenten überhaupt hinbekommen hab.

Hier ist der Code:

Code

import lotus.domino.*;
import java.sql.*;
import java.io.*;
import java.util.*;

public class JavaAgent extends AgentBase {

	public void NotesMain() {

		try {
			Session session = getSession();
			AgentContext agentContext = session.getAgentContext();
			Database db = agentContext.getCurrentDatabase();
			Document doc = agentContext.getDocumentContext();
			Document profildok = db.getProfileDocument("Profildok", "");
			
			// Variablen deklarieren
			String l_Feld1;
			String l_Feld2;
			String l_Feld3;
			boolean rc;
			
			// Nr. aus Profildokument auslesen
			l_Feld1 = profildok.getItemValueString("Nr");
			
			
			PrintWriter pw = getAgentOutput();
			Connection con=null;
			Statement stmt=null;
			ResultSet rs=null;
			
			String JDBCDriverName="Treibername";
			String system="Pfad";
			String user = "username"; 
			String pwd = "passwort"; 

			DriverManager.registerDriver((Driver) Class.forName(JDBCDriverName).newInstance());
			con = DriverManager.getConnection(system,user,pwd);
			stmt = con.createStatement();
			
						
			// Abfrage
			String sql = "SELECT ...;
			rs = stmt.executeQuery(sql);
			
			// prüfen ob rs leer ist
			rc = rs.next();
				if (rc) {
			// Felder auslesen
			l_Feld1 = rs.getString(1);
l_Feld2 = rs.getString(2);
l_Feld3 = rs.getString(3);
										}
		else {
										l_Feld1 = "";
		l_Feld2 = "";
		l_Feld3 = "";
					
			};							
			if (z) {
			// neues Dokument anlegen		
			doc = db.createDocument();
			doc.appendItemValue("Form", "Dok");
			System.out.println("Dok. angelegt.");
			
			// Felder im Dokument füllen
			doc.appendItemValue("NFeld1", l_Feld1);
			doc.appendItemValue("NFeld2", l_Feld2);
			doc.appendItemValue("NFeld3", l_Feld3);
			// Dokument speichern
			doc.save(true, true);
			}
			else {
							}

			// Close statement and connection
			rs.close();
			stmt.close();
			con.close();
			} 
			catch (Exception e) {
				e.printStackTrace();
				}
	}
}



Bin für jeden Tipp dankbar.
Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 08:43:29
Hallo!

Ich empfehle dir auf jeden Fall das Datenbankobjekt in deinem Agent mit db.recycle() zu löschen.

Zur kurzen Erklärung, die Notes Java Klassen sind nur ünne Hüllenklassen für das darunterliegende C++ Api. das heisst, wenn du in Java ein DominoDatenbankObjekt erstellst, wird im Hintergrund zusätzlich zu deinem Java Objekt auch ein C++ Objekt erstellt. Diese C++ Objekte werden aber vom Java Garbage collector nicht gesehen und daher auch nicht automatisch bereinigt. Deshalb ist es wichtig wenn du in Domino Java Programmen oder Agenten Domino Backendobjekte verwendest die auch wieder ordentlich bereinigst. Dafür hat jedes Objekt die recycle Methode.

Hört sich schlimmer an, als es ist denn die recycle Methode hat die praktische Funktion, das wenn du ein Objekt bereinigst, sämtliche abhängigen Objekte mit recycelt werden. Das heisst, wenn du aus einem Datenbankobjekt eine View oder ein Dokument erstellst, dann werden auch die bereinigt wenn du das Datenbankobjekt recycelst. Deshalb ist es in Java Programmen (nicht jedoch Agenten) Best Practise zum Schluß auf jeden Fall die session zu recyceln. In Java Agenten ist es jedoch so, dass di Session die Session deines NotesClients ist und wenn du die recycelst, wünsche ich dir gleich viel Spaß, denn dann treten die seltsamsten Phänomene auf.

Ich hoffe, das hilft dir falls nicht einfach nochmal fragen.

Grüße

Ralf

P.S. Eventuell kann das wer von den Mods ins Java Forum verschieben.
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 22.04.05 - 09:07:33
Versuch erstmal Ralfs tipp.
Also:

am Schluss:
Code
con.close();
db.recycle();
}
catch (Exception e) {
e.printStackTrace();
if (con != null) con.close(); // was anderes, aber wo du schon dabeibist.
if (db != null) db.recycle();
}

OBEN:
Deklaration von con und db oberhalb des trys schieben:
Code
Database db = null;
Connection con = null;
try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
// auskomentiert Database db = agentContext.getCurrentDatabase();
Document doc = agentContext.getDocumentContext();
Document profildok = db.getProfileDocument("Profildok", "");
[... weiter unten]
PrintWriter pw = getAgentOutput();
// auskommentiert : Connection con=null;

Desweiteren kann das errorhandling verbessert werden, aber dazu später mehr.

Es liegt nicht an Java. Die Integration von Java und Notes ist ein bischen ätzend. Ich  programmiere pro Woche durchschnittlich 20 neue SQLConnections low level oder mit wechselnden Frameworks, Tools (Ant, ibatis sqlmaps, springframework-jdbc, hibernate, ejb) und laufe da einfach nicht in eine OutOfMemory.  >:(
Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 09:21:26

Es liegt nicht an Java. Die Integration von Java und Notes ist ein bischen ätzend. Ich programmiere pro Woche durchschnittlich 20 neue SQLConnections low level oder mit wechselnden Frameworks, Tools (Ant, ibatis sqlmaps, springframework-jdbc, hibernate, ejb) und laufe da einfach nicht in eine OutOfMemory. >:(

War ich früher auch der Meinung, aber ganz richtig ist es nicht. Es liegt insofern an Java, das Java nicht gewährleistet, dass die finalize Methode eines Objekts aufgerufen wird, wenn das Objekt garbage collected wird. Wenn du also mit Native peers arbeitest, dann hast du dieses recycle() Problem in irgendeiner Form. So was ähnliches gibt es meines Wissens ja auch bei SWT.

Ich hoffe birdy gerhört nicht zu der Sorte die nie weider etwas posten nach dem Sie eine Frage gestellt haben.

Grüße

Ralf
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 09:44:09
Keine Sorge, lass mich so schnell nicht vertreiben ;-)

Also danke erstmal für eure Tipps. Das mit dem recycle hab ich eingebaut, hat leider nicht geholfen.

Code
profildok.recycle();
			doc.recycle();
			db.recycle();
			agentContext.recycle();
			session.recycle();

Das Deklarieren von Database und Connection hab ich oberhalb des trys platziert. Allerdings bekomme ich zu den beiden If-Clauses am Ende des Codes einen Fehler bei der Kompilierung ???

Fehler:
Exception java.sql.SQLException must be caught, or it must be declared in the throws clause of this mehtod.

Code
public void NotesMain() {
		
		Database db = null;
		Connection con = null;

		try {
			Session session = getSession();
			AgentContext agentContext = session.getAgentContext();
			db = agentContext.getCurrentDatabase();
			Document doc = agentContext.getDocumentContext();
			Document profildok = db.getProfileDocument("Profildok", "");

Code
catch (Exception e) {
				e.printStackTrace();
			if (con != null) con.close();
			if (db != null) db.recycle();
				}


Hat mich bisher leider nicht weitergebracht.
Fällt euch vielleicht noch was ein?
Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 10:14:27
Hallo!

Bitte Posts genau lesen!!! ich habe bereits geschrieben, dass man die Session in Agenten nicht recyceln darf. Nur die Objekte recyceln die du selbst erstellt hast. Die Session in Agenten ist eine Session die von Lotus Notes kommt.

hier ein Auszug aus der Agent FAQ auf Notes.net

For any agent even when you need recycle for some things you don't ever need to recycle these two things:
agentContext.recycle();
session.recycle();

Zu deinen Compile Problemen. Jede Methode eines DominoBackend Objekts wirft eine Notesexception im Fehlerfalle und muß daher in einer Try catch Klammer sein.

Übrigens, habe offensichtlich einen Fehler gemacht, du darfst in deinem Fall auch die Datenbank nicht recyceln, da diese aus dem AgentContext kommt.  Recyceln darf man nur Objekte die man selbst erstellt hat und wenn man im AgentContext auf currentDatabase zugreift, bekommt man keine neue Datenbank sondern die im AgentContext bestehende Datenbank.

Hoffe das hilft, übrigens welche Version setzt du ein und läuft der Agent auf dem Client oder Server?

Grüße

Ralf
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 10:35:42
Sorry Ralf, war auch im IBM-Forum unterwegs und da hat jemand auch agentContext und session recycelt, deshalb dachte ich kann ja nicht schaden.

Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 10:37:05
No Problem, wie schaut es aus, hast du erfolg gehabt?

Sonst hätte ich noch weitere Ideen.

Grüße

Ralf
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 10:45:31
Der Agent läuft auf dem Client und die Notes-Verion ist 6.5.2

Das Problem besteht immer noch. :-\
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 22.04.05 - 10:48:14
Oh ich dummy.

Fehler:
Exception java.sql.SQLException must be caught, or it must be declared in the throws clause of this mehtod.

Bitte ersetze im catch.
Code
catch (Exception e) {
				e.printStackTrace();

			if (con != null) 
try {
con.close();
} catch (SQLException sqle) {
// einer der wenigen Fälle, wo leere catch - clauses ok sind. IST SONST EINE SEHR SCHLECHTE IDEE!!!
}
			if (db != null) db.recycle();
				}


Hat mich bisher leider nicht weitergebracht.
Fällt euch vielleicht noch was ein?
Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 10:48:38
Ok, dann gehe ich davon aus, dass es nicht am C++ Backend liegt, das haben wir ja jetzt ausgeschaltet, eventuell läuft deine Garbage Collection nicht richtig. Setz mal alle größeren Objekte in deinen Agent zum Schluß auf null.

Also:

rs=null;
stmt=null;
con=null;

usw.
und

dann bau noch

System.gc();

Das sollte die Garbagge Collection der Notes Jvm auslösen. Ich hoffe, deine Probleme sind dann behoben. Noch eine Frage, wie hast du denn den JDBC Treiber eingebunden? Direkt importiert, oder in JavaUserClasses in der Notes.ini?


Grüße

Ralf
Titel: Re: Java OutOfMemoryError
Beitrag von: qojote am 22.04.05 - 11:00:39
Hi,

ich hatte daselbe Problem wenn ich den die Sachen in den Agent als Attachement gepackt habe.
Auch der Anwendungsfall ist der selbe JDBC auf eine DB2.
Einziger Unterschied ich habe einen Backendagent auf dem Server genutzt
Ich habe jetzt meine jt400.jar die ich für den JDBC Zugriffbrauche auf den Server gelegt und habe seitdem keine Probleme mehr.

Gruß
Qojote
Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 11:14:10
Genau war auch meine Vermutung. Blöd wäre es nur wenn er es am Client braucht, denn dann muß er sich was ein fallen lassen wie er die JT400.jar auf die Clients deployed warten wir mal ab.

Grüße

Ralf
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 11:39:19
Hallo Ralf,

hab alles auf null gesetzt am Ende und System.gc() eingebaut, ohne Erfolg.

Hallo qjote,

der Treiber für die AS400 liegt auf dem Server, daran kanns also auch nicht liegen.

Danke trotzdem für alle eure Tipps und Mühe. Trau mich ja gar nicht mehr fragen, ob ihr sonst noch Ideen habt  :-[

LG, Birgit
Titel: Re: Java OutOfMemoryError
Beitrag von: Ralf_M_Petter am 22.04.05 - 11:41:45
Jetzt nochmal langsam, was heisst, kein Erfolg. du bekommst also noch immer die Fehlermeldung OutofMemoryError. Kannst du mal den genauen Inhalt der Javakonsole posten?

Grüße

Ralf

P.S. Eventuell poste nochmal deinen jetzigen Code und deine genaue Vorgehendsweise.
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 22.04.05 - 11:56:49
Hi,

ich hatte daselbe Problem wenn ich den die Sachen in den Agent als Attachement gepackt habe.
Guter Punkt  :D
In deinem Programmverzeichnis müsste sich ein VErzeichnis java\lib\ext (oder so ähnlihc) befinden.
Tu da den DB2Treiber rein und starte den Notes Client neu.
Lösche dann den DB2Treiber aus dem Attachment und kompiliere neu.
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 12:09:06
Yep, die Fehlermeldung ist immer noch die gleiche.

Die Vorgehensweise im Detail:

1. Der User gibt über eine Dialogbox eine Auftragsnr. ein.
2. Die Nr. wird in einem Profildokument gespeichert.
3. Der Java-Agent wird aufgerufen.

 - bis hierher alles über LotusScript -

4. Der Agent liest die Nr. aus dem Profildokument aus.
5. Es wird über JDBC eine Verbindung zur DB2 aufgebaut.
6. SQL-Abfrage
7. Im RecordSet wird nach der Nr. gesucht.
8. Anhand der Auftragsnr. werden mehrere Daten aus der DB2 gefischt und in Variablen gespeichert.
9. Ein neues Notes-Document wird erstellt.
10. Die Daten werden an Felder im Notes-Doc übergeben.
11. Das Notes-Doc wird gespeichert.
12. Die JDBC-Verbindung wird gekappt.
13. Objekte werden recycelt und alles auf null gesetzt.

Hier nochmal der Code:

Code
import lotus.domino.*;
import java.sql.*;
import java.io.*;
import java.util.*;

public class JavaAgent extends AgentBase {

	public void NotesMain() {
		
		Database db = null;
		Connection con = null;

		try {
			Session session = getSession();
			AgentContext agentContext = session.getAgentContext();
			db = agentContext.getCurrentDatabase();
			Document doc = agentContext.getDocumentContext();
			Document profildok = db.getProfileDocument("Profildok", "");
			
			// Variablen deklarieren
			String l_nr;
			String l_artikel;
			String l_stueck;
			String l_text;
			String l_termin;
			boolean rc;
			
			// Nr. aus Profildokument auslesen
			l_nr = profildok.getItemValueString("Nr");
			
			System.out.println("lfd.Nr. " + l_lfdnr);

			PrintWriter pw = getAgentOutput();
			// Connection con=null;
			Statement stmt=null;
			ResultSet rs=null;
			
			String JDBCDriverName="com.ibm.as400.access.AS400JDBCDriver";
			String system="Serverpfad/; libraries=libname";

			String user = "username"; 
			String pwd = "password"; 

			DriverManager.registerDriver((Driver) Class.forName(JDBCDriverName).newInstance());
			con = DriverManager.getConnection(system,user,pwd);
			stmt = con.createStatement();
			
			System.out.println("Connection hergestellt.");
			
			// SQL-Abfrage
			String sql = "SELECT feld1, feld2, feld3, feld4 FROM libname.tablename;
			rs = stmt.executeQuery(sql);
			
			// prüfen ob rs leer ist
			rc = rs.next();
				if (rc) {
					// Felder auslesen
					System.out.println("RS gefüllt");
					l_nr = rs.getString(1);
					l_artikel = rs.getString(2);
					l_stueck = rs.getString(3);
					l_termin = rs.getString(4);
					l_text = rs.getString(5);
					}
				else {
					// Leerstring
					System.out.println("RS leer");
					l_nr = "";
					l_artikel = "";
					l_stueck = "";
					l_termin = "";
					l_text = "";
					};
			 
			// Anzahl der Zeichen zählen, wenn < 0 ist Leerstring
			int l_zeichen = l_artikel.length();
			System.out.println("Zeichen " + l_zeichen);

			boolean z = (l_zeichen > 0);
			
			if (z) {
			// neues Dokument anlegen		
			doc = db.createDocument();
			doc.appendItemValue("Form", "maskenname");
			System.out.println("Dok. angelegt.");
			
			// Felder im Dokument füllen
			doc.appendItemValue("Nr", l_nr);
			doc.appendItemValue("Artikel", l_artikel);
			doc.appendItemValue("Stueck", l_stueck);
			doc.appendItemValue("Termin", l_termin);
			doc.appendItemValue("Text", l_text);
						
			// Dokument speichern
			doc.save(true, true);
			}
			else {
				System.out.println("Kein Dokument angelegt.");
			}
			
			// Close statement and connection
			rs.close();
			stmt.close();
			con.close();
			
			// alles bereinigen
			profildok.recycle();
			doc.recycle();		
			
			rs = null;
			stmt = null;
			con = null;
			
			System.gc();

			} //end try


			catch (Exception e) {
				e.printStackTrace();
			
			if (con != null)
			try{
				con.close();
				} catch (SQLException sqle) {
					}
				}
	}
}
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 22.04.05 - 12:13:30
@marinero atlantico

das Verzeichnis hab ich.
nur, was meinst du mit Attachment?

und, müsste dann jeder user der die db nutzt diesen treiber im verzeichnis stehen haben? fürchte da spielt mein admin nicht mit.
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 22.04.05 - 12:47:26
Das die fEhlermeldung kommt verstehe ich nicht, aber es fehlte die Klammerung nach dem if in der catch clause. Mit // NEU gekennzeichnet.
Code
import lotus.domino.*;
import java.sql.*;
import java.io.*;
import java.util.*;

public class JavaAgent extends AgentBase {

	public void NotesMain() {
		
		Database db = null;
		Connection con = null;

		try {
			Session session = getSession();
			AgentContext agentContext = session.getAgentContext();
			db = agentContext.getCurrentDatabase();
			Document doc = agentContext.getDocumentContext();
			Document profildok = db.getProfileDocument("Profildok", "");
			
			// Variablen deklarieren
			String l_nr;
			String l_artikel;
			String l_stueck;
			String l_text;
			String l_termin;
			boolean rc;
			
			// Nr. aus Profildokument auslesen
			l_nr = profildok.getItemValueString("Nr");
			
			System.out.println("lfd.Nr. " + l_lfdnr);

			PrintWriter pw = getAgentOutput();
			// Connection con=null;
			Statement stmt=null;
			ResultSet rs=null;
			
			String JDBCDriverName="com.ibm.as400.access.AS400JDBCDriver";
			String system="Serverpfad/; libraries=libname";

			String user = "username"; 
			String pwd = "password"; 

			DriverManager.registerDriver((Driver) Class.forName(JDBCDriverName).newInstance());
			con = DriverManager.getConnection(system,user,pwd);
			stmt = con.createStatement();
			
			System.out.println("Connection hergestellt.");
			
			// SQL-Abfrage
			String sql = "SELECT feld1, feld2, feld3, feld4 FROM libname.tablename;
			rs = stmt.executeQuery(sql);
			
			// prüfen ob rs leer ist
			rc = rs.next();
				if (rc) {
					// Felder auslesen
					System.out.println("RS gefüllt");
					l_nr = rs.getString(1);
					l_artikel = rs.getString(2);
					l_stueck = rs.getString(3);
					l_termin = rs.getString(4);
					l_text = rs.getString(5);
					}
				else {
					// Leerstring
					System.out.println("RS leer");
					l_nr = "";
					l_artikel = "";
					l_stueck = "";
					l_termin = "";
					l_text = "";
					};
			 
			// Anzahl der Zeichen zählen, wenn < 0 ist Leerstring
			int l_zeichen = l_artikel.length();
			System.out.println("Zeichen " + l_zeichen);

			boolean z = (l_zeichen > 0);
			
			if (z) {
			// neues Dokument anlegen		
			doc = db.createDocument();
			doc.appendItemValue("Form", "maskenname");
			System.out.println("Dok. angelegt.");
			
			// Felder im Dokument füllen
			doc.appendItemValue("Nr", l_nr);
			doc.appendItemValue("Artikel", l_artikel);
			doc.appendItemValue("Stueck", l_stueck);
			doc.appendItemValue("Termin", l_termin);
			doc.appendItemValue("Text", l_text);
						
			// Dokument speichern
			doc.save(true, true);
			}
			else {
				System.out.println("Kein Dokument angelegt.");
			}
			
			// Close statement and connection
			rs.close();
			stmt.close();
			con.close();
			
			// alles bereinigen
			profildok.recycle();
			doc.recycle();		
			
			rs = null;
			stmt = null;
			con = null;
			
			System.gc();

			} //end try


			catch (Exception e) {
				e.printStackTrace();
			
			if (con != null) { //NEU NEU NEU (die Klammer)
			try{
				con.close();
				} catch (SQLException sqle) {
					}
				}
	}
}
Kanns z.Zt. nicht ausprobieren.
Ja. Der Treiber müsste dann auf jeden Desktop deployed werden.
3-Tier Umgebungen sind mit Notes ein bischen nicht so gut.
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 23.04.05 - 01:24:48
Hier zu später Stunde noch das versprochene verbesserte ExceptionHandling:
Über
Code
catch (Exception e) 
das hier:
Code
catch(SQLException sqlEx)
    {
      while(sqlEx != null) {
        System.err.println("SQLException information");
        System.err.println("Error msg: " + sqlEx.getMessage());
        System.err.println("SQLSTATE: " + sqlEx.getSQLState());
        System.err.println("Error code: " + sqlEx.getErrorCode());
        sqlEx.printStackTrace();
        sqlEx=sqlEx.getNextException();
      }
    }
Falls es Probleme mit dem SQL gibt, erhält man so alle wichtigen Fehlerinformationen. Über SQLState und ErrorCode bekommt man aus der DB2 Doku direkt raus, woran es hakt. Meistens ist bei mir das SQL-Statement doch nicht so richtig (bei mir zumindest)

Btw. bringt System.gc() fast nie etwas.

Ausserdem solltest du PreparedStatement statt Statement verwenden. Das macht den Code oft sicherer und vielleicht können so auch die Ausführungspfade von SQL-Statements auf DB2 gecached werden (die Regeln habe ich immer noch nicht so richtig verstanden). JDBC läuft immer über sogenanntes "dynamisches SQL" und da wird auf DB2 immer zuerst der "Ausführungsplan" errechnet und das nimmt Zeit in Anspruch. Bei einfachen SQL aber im Milisekundenbereich.

Warum diese komische Konstruktion mit connection noch mal in catch schliessen?
Oben öffnest du eine Datenbank-Connection. Das ist eine knappe Ressource. Wenn nun in dem Code des try-Blocks an irgend einer Stelle eine Exception auftritt, wechselt das Programm direkt in den catch-Block und kommt nie wieder in den try zurück. Die Connection muß aber geschlossen werden. So muß man das eben im catch auch noch machen. Und dieses Statement muß wiederum mit try-catch umgeben werden, da connection.close() eine SQLException wirft.

Das sind all diese kleinen JDBC-Basics.

In der realen Java-Welt ist JDBC Programmierung mittlerweile ziemlich selten. Die Leute benutzen irgendwelche Frameworks und Objekt-Relationalen-Mapper wie Hibernate.
Ich hab hier z.B. ein Framework namens IBAtis SQLMaps und da stehen die ganzen SQL-Queries gar nicht mehr im Source-Code sondern werden per xml definiert:
Sieht ungefähr so aus:
Als Beispiel steht ein solcher sql-query in einer xml-Datei (mit den entsprechenden Datentypen die übergeben und zurückgegeben werden:
Code
<select id="getIdByIdUserAndIdStockDescr"
		parameterClass="java.util.HashMap" resultClass="java.lang.Long">
		Select ID from STockUser where IDUSER=#idUser# AND IDStockDescr=#idStockDescr#
</select>
Wird dann einfach so in einem sogenannten DataAcces Object aufgerufen und ich spar mir den ganzen restlichen Quatsch. Den Rest macht das Framework. Hier lass ich mir nur eine ID als Long zurückgeben. Ganze Objekte geht aber auch. Auch als Collection.
Code
public StockUser getStockUserByIdUserAndIdStockDescr(long idUser, int idStockDescr) throws IllegalArgumentException {
		if (idUser < 0) throw new IllegalArgumentException("Parameter idUser cannot be lower than 0");
		if (idStockDescr < 0) throw new IllegalArgumentException("Parameter idStockDescr cannot be lower than 0");
		
		Map params = new HashMap();
		params.put("idUser", new Long(idUser));
		params.put("idStockDescr", new Long(idStockDescr));
		return (StockUser)getSqlMapClientTemplate().queryForObject("getStockUserByIdUserAndIdStockDescr", params);
	}

Leider kann man mit Notes solche schönen Sachen nur unter ziemlichen Verrenkungen schwer verwenden.

Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 26.04.05 - 08:55:39
Hi Marinero,

danke für die Nachhilfe in ExceptionHandling. Habs eingebaut.

Viel weiter gekommen bin ich bisher leider noch nicht. Gibt es Informationsquellen für Java-Anfänger wie mich, in denen z.B. die Vorgehensweise bei JDBC-Connection beschrieben wird.

Ich hab den Agenten nämlich 1:1 so übernommen und nur geringfügig angepasst, also weiß ich auch nicht wirklich, wie ich es vielleicht besser machen könnte. Auch das mit diesen PreparedStatements würde mich interessieren. Wo kann ich mehr drüber erfahren?

Zu meinem SQL-Statement noch ne Frage. Ich habe meine SQL-Abfrage in 3 Schritten gemacht, d.h. ich krieg 3x nen ResultSet. Liegt hier vielleicht ein Fehler, muss ich jedesmal den ResultSet und das Statement wieder schließen? Ich hab das versucht, allerdings krieg ich dann auch wieder ne Fehlermeldung.

LG, Birgit
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 26.04.05 - 11:26:34
Hi,

hab da nochmal ne Frage.

Wäre es ne Möglichkeit, die JDBC-Connection solange aufrecht zu erhalten, bis die DB vom User geschlossen wird?
Also mit 1. Aufruf des Agenten wird die Verbindung aufgebaut und muss dann nicht jedesmal wieder hergestellt werden, wenn mehrmals hintereinander Daten abgerufen werden.

Oder spielt das keine Rolle?

Hab absolut keine Ahnung, was ich noch tun könnte.

Birgit
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 26.04.05 - 11:55:53
Hallo Birgit.

Zu meinem SQL-Statement noch ne Frage. Ich habe meine SQL-Abfrage in 3 Schritten gemacht, d.h. ich krieg 3x nen ResultSet.
vielleicht kannst du das SQL so optimieren, dass du mit einem Query alle Daten auf einmal bekommst.
Da geht ja mit inner joins, outer joins, subqueries und vieles andere mehr so einiges.
Auch Views und Stored Procedures auf DB-Seite sind zu beachten, wenn es darum geht SQL-Statements einfacher und performanter zu machen. Daneben gibt es noch Temporary Tables, die ich aber z.B. ignoriere. 
Zumindest mit DB2 Version 8 ist der SQL Builder auch nicht ganz schlecht. Damit kann man sich ganz gut komplexere Dinge zusammenklicken und erhält die entsprechende SQL Syntax.   
Du kannst gerne die 3 SQL Statements posten.
Liegt hier vielleicht ein Fehler, muss ich jedesmal den ResultSet und das Statement wieder schließen? Ich hab das versucht, allerdings krieg ich dann auch wieder ne Fehlermeldung.
Sobald du das alte Resultset geschlossen hast, kannst du nicht mehr drauf zugreifen. Du kannst ruhig das alte offenlassen. Das sollte keine große Auswirkung auf die Performance haben. Bin mir nicht ganz sicher, aber ich glaub, dass DB2 JDBC Treiber zumindest ab Version 7 DB2 Resultset und Statement automatisch schliessen, wenn du die Connection schliesst, mit der diese beiden erzeugt worden sind. Das ist zumindest das normale Verhalten von modernen JDBC Treibern. Du mußt auf jeden Fall die Connection schliessen und ZWAR INNERHALB DES AGENTEN, IN DEM DU SIE GEÖFFNET HAST. DAS IST QUASI NATURGESETZ.
1. Kannst du nicht sicher sein, ob der 2. Agent überhaupt läuft (und OFFENE CONNECTIONS SIND BÖSE
2. Ist für die Performance eine lange offene Connection gar nicht gut, besonders wenn mehrere User drauf zugreifen. Man soll die Connection immer nur für möglichst kurze Zeit offen halten und AUF JEDEN FALL IN DER ROUTINE SCHLIESSEN, IN DER SIE AUFGERUFEN WURDE.
Es gibt Fälle, wo es wünschenswert ist, eine Datenbanktransaktion über mehrere UserScreens laufen zu lassen.
Bsp:
Buche Platz in Flugzeug
Gebe Kredikarteninfo
Bezahlen.
Dem User werden nacheinander 3 Seiten geschickt, wo er was anklickt. Auf Datenbankebene wird es aber selbst dort 3 Connections geöffnet und geschlossen (1 pro Screen des Users). Gleiches gilt für Datenbanktransaktionen.
Um das konsistent zu halten, behilft man sich mit Tricks. Am bekanntesten sind optimistic und pessimistic locking.
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 26.04.05 - 12:05:28
Schön, dass du wieder da bist. :)

Hab mein SQL-Statement auch schon mittels INNER JOIN auf 1 Query reduziert, aber da kams mir so vor, als würde es noch länger dauern, bis ich ein Ergebnis bekomme.
Nachdem der Fehler so oder so auftritt, bleibe ich jetzt bei meinen 3 Schritten.

Das mit der Connection seh ich ein, greife halt nach jedem Strohhalm.

Werde als nächstes mal die jt400.jar in mein jvm/lib/ext Verzeichnis einbinden. Vielleicht hilft das ja.

Ihr werdet sicherlich wieder von mir hören ;-)

LG, Birgit
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 26.04.05 - 12:23:16
Prüfe die Indexe in den Tabellen. Für alle Spalten, auf die die Joins gehen, muß es in der Datenbank einen Index geben. Frage den Datenbankadministrator. Falls es einen gibt.
Dies sollte die Performance deutlich verbessern.
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 26.04.05 - 12:43:16
[kurzer_zwischen_rant]  >:(
In sämtlichen mir bekannten Java Umgebungen ausser Lotus Notes kann man btw sogenannte ConnectionPools benutzen. Da muß man Connections auch schliessen, nur werden sie da an den besagten Pool zurückgegeben.
Das ist seit ca. 2001 Standard.
Mittlerweile gibt es auch alle möglichen caching Lösungen (von sehr einfach--> ich selbst benutz z.B. einfach eine HashMap bis sehr sophisticated und verteilt).
Weder Connection Pools noch diese Caches kann man aus architekturellen Gründen in Domino Agenten benutzen.
Imho sind solche Dinge der rationale Grund, warum Lotus überhaupt mit Workplace angefangen hat.
 
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 26.04.05 - 14:43:45
 ;D ;D ;D

Ich kann gar nicht sagen, wie froh ich bin, mein Problem scheint gelöst zu sein.
Habe die beiden jar-Files, die im Agent eingebunden waren, in mein lokales Verzeichnis jvm/lib/ext gepackt und jetzt scheint es zu laufen. Habe den Agenten jetzt bestimmt 20x hintereinander laufen lassen und es kam keine Fehlermeldung!!! Vorher war schon bei 6-7x Ende Gelände.
Und das beste an der Sache, jetzt gehts sogar schneller als vorher!

Ich hab leider etwas gebraucht, bis ich gecheckt hab, was mit diesem Attachment gemeint ist. Nachdem ich den Agenten schon fast fertig bekommen habe, wusste ich nicht, dass da zwei jar.-Dateien anhingen. Aber man lernt ja nie aus.

Also vielen Dank an alle, die sich hier für mich den Kopf zerbrochen haben. Ihr wart mir wirklich ne große Hilfe. Qojote hatte ja bereits zu Beginn geschrieben, wie es geht, ich stand nur sehr auf der Leitung.

So long, machts gut und bis zum nächsten Prob (hoffe nicht allzu bald).

LG, Birgit
Titel: Re: Java OutOfMemoryError
Beitrag von: qojote am 27.04.05 - 08:39:14
Hi,

schön dass es jetzt geht.
Ich hatte leider die letzten Tage kaum Zeit sonst hätte ich mein Posting nochmal vertieft.
Aber Marinero hat ja wie immer sehr gute Hilfestellung geleistet.

Gruß
Qojote
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 27.04.05 - 10:09:18
Bleibt noch die Frage wie du die db2libs verteilst.
Java Client Umgebungen wie Java Web Start und Eclipse RCP verfügen über Elemente, die das automatisch machen. Neuinstallation, Check ob neues Release, automatischer Download von neuem Release.
Vielleicht kennt der Administrator ja eine Möglichkeit, um das automatisch mit Windowsmitteln zu verteilen.
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 27.04.05 - 10:56:02
Hallo zusammen,

ich schon wieder. Hab mich wohl zu früh gefreut.
Das mit den lokalen jar.-Dateien funktioniert nur bei den Windows2000-Rechnern. Haben aber auch Windows XP im Einsatz und da läufts nicht.

Könnt langsam durchdrehen. Folgendes steht in der Java-Debug-Konsole:

java.lang.ClassNotFoundException: com.ibm.as400.access.AS400JDBCDriver
   at lotus.domino.AgentLoader.loadClass(Unknown Source)
   at java.lang.ClassLoader.loadClass(ClassLoader.java:448)
   at java.lang.Class.forName1(Native Method)
   at java.lang.Class.forName(Class.java:142)
   at JavaAgent.NotesMain(JavaAgent.java:52)
   at lotus.domino.AgentBase.runNotes(Unknown Source)
   at lotus.domino.NotesThread.run(NotesThread.java:215)

Hat jemand ne Idee, was ich jetz machen kann???
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 27.04.05 - 11:40:23
Versuch mal den Notes Client auf WinXP herunterzufahren und neu zu starten. Es kann daran liegen.

Gruß Axel
Titel: Re: Java OutOfMemoryError
Beitrag von: qojote am 27.04.05 - 12:23:34
Hi,

du kannst alternativ den Pfad zu den Dateien in der Notes.ini
unter JavaUserClases="c:\xxx" eintragen.
Hat den Vorteil das du den Pfad selbst bestimmen kannst.

So hab ich das an meinem Client gemacht das mit dem ext Verzeichnis hab ich nur am Server genutzt.

Gruß
Qojote
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 27.04.05 - 12:39:16
... das geht auch. Versuch aber bitte erstmal den Notes-Client runter- und hochzufahren. Oder am besten das ganze XP.
Falls der im Windows XP nicht das ext Verzeichnis finden würde, wäre das sehr - im Sinne von völlig - ich glaubs nicht - merkwürdig.
Das der Client noch mal hoch- und runtergefahren werden muß, ist normal.
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 27.04.05 - 13:03:03
Hi Ralf,

will ja noch nix verschreien, aber scheint zu funktionieren.
Habs jetz an 2 Rechnern probiert mit dem Neustart und hat geklappt.

Danke für den Tipp, hätt ich ja mal wieder aus selbst drauf kommen können.

Hab grad schon ein Glas Sekt auf euch getrunken in meiner Mittagspause ;o)

LG, Birgit
Titel: Re: Java OutOfMemoryError
Beitrag von: birdy am 27.04.05 - 13:04:21
Ups, vertippt, bist ja der Marinero. Sorry
Sekt schon zur Mittagszeit bekommt mir wohl nicht.
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 28.04.05 - 00:07:59
Glückwunsch
Ich mach ja momentan das umgekehrte.
Ich will aus Performancegründen RDBMS-Hits vermeiden und statt dessen mit einem cache kommunizieren.
Könnte ich ja auch pro gesparten Select gegen RDBMS eine Flasche Sekt trinken.
Diese Woche wäre ich bislang stocknüchtern geblieben (mach das aber nur abends).
Titel: Re: Java OutOfMemoryError
Beitrag von: Teletambi am 04.06.05 - 04:07:22
Hallo,

hast du vlt. schon einmal versucht deinen JavaCode in einem "ordentlichen" try-catch-finally Block unterzubringen und mehrere Execptions (NotesException, SQLException sowie Exception) in einem jeweils separaten catch unterzubringen? Die Aufrufe für resSet.close(), stmt.close() etc. gehören dann in das abschließende finally.
Da diese wiederum eine Exception werfen können, müssen diese auch in einem try-catch Block enthalten sein.

Ich hatte ein ähnliches Problem mit der JDBC-Programmierung und konnte anfangs keine Ursache dafür finden bis ich alle möglich Exceptions mit  der entsprechende Fehlermeldungausgabe behandelt habe.

Ich nehme mal ganz stark an, dass irgendwo eine Exception auftritt, welche du in deinem Code nicht behandelt hast.

MfG Teletambi
Titel: Re: Java OutOfMemoryError
Beitrag von: Marinero Atlántico am 04.06.05 - 11:22:08
@Teletambi: Dieser Thread war bereits als gelöst gemeldet.
Trotzdem weisst du auf eine sehr wichtige Sache hin.
Ist mir damals entgangen, wobei ich mich in dem Punkt "external Ressourcen schliessen" bisher für paranoid gehalten habe.

Schliessen der Connection im finally (oder meine traditionelle Lösung s.u.) finde ich auch besser, weiss nicht, warum ich damals nicht darauf hingewiesen habe. Das wurde hier am Ende des try und im catch Block geschlossen, was nicht perfekt ist, hier aber offenbar ausreichend.
Danke aber für deinen Hinweis, weil du in gewissen Punkten sicher recht hast.

Jede Exception *einzeln* zu catchen halte ich hingegen für unnötig.
Schliesslich haben Exceptions in Java eine gewisse Vererbungshierarchie, die man sich zu Nutze machen kann.
Wenn du eine gewisse Menge an Funktionalität/per Stunde programmierst, hat man in der Praxis keine Zeit, jede Exception einzeln zu catchen. Statt dessen sollte man sich Gedanken machen, wo spezifisches Exception Handling Sinn macht. Wo nicht, sollte man die Superklassen verwenden (s.u.)

In Java gibt es 2 Arten von Exceptions.
Checked und Unchecked.
Die Checked sind die, wo der Compiler immer anmeckert, dass man diese Exception abfangen muss. Alle checked Exceptions erben von der Klasse java.lang.Exception.
Unchecked Exceptions müssen nach Meinung des Compilers dagegen nicht per catch abgefangen werden. Das sind oft exception, die mit dem Wissen des Compilers eigentlich immer auftreten können. Z.B. NullPointerException. Der Compiler weiss nicht, ob später bei der Ausführung des Programms eine Referenz auf ein Objekt weist oder nicht.
 
Um sicher zu gehen, dass ein catch-Block für alle Arten von Exceptions zuständig ist, kann man java.lang.Throwable nutzen.  Throwable ist die Superklasse von Exception, RuntimeException und Error ist (wobei RuntimeException und Error Superklassen aller unchecked Exceptions sind).

Wenn man z.B. mit externen Ressourcen arbeitet, kann es imho Sinn machen mit Throwable zu arbeiten. Oder man schliesst sie eben im finally.
Über dem Throwable können dann spezifischere Exceptions abgefangen werden.

z.B.:
Code
try {
...
} catch (NotesException e) {
... do stuff ..
}
catch (SQLException e) {
... do stuff ..
}
catch (Throwable t) {
/* würde man hier, in allen darüberliegenden catches und im try die Connections schliessen wäre das safe. Man kann hierfür eine extra Methode vorsehen. So mach ich das immer aus Gewohnheit. */
... do stuff ...
} finally {
/* würde man hier die Connections schliessen und im try nicht wäre das auch safe und weniger Tipparbeit. Vielleicht sollte ich meine Gewohnheit ändern. Wobei ich für DB-Zugriff Frameworks (s.u.) nutze, wo ich kann: Heisst überall ausser in Java in Notes. */
}

In der Tat ist gerade das Exception-Handling und auch Probleme mit stalled Connections (die man in den Griff kriegen kann) Punkte, die oft an der Architektur von JDBC kritisiert worden sind.
Dies ist ein eher unwichtiger Grund, warum die Leute so gerne Frameworks wie Hibernate oder ibatis SQLMaps statt low-level JDBC arbeiten, da diese Frameworks diese und andere Probleme schon gelöst haben. 

Ach ja. Und bei OutOfMemoryError extends java.lang.Error wird natürlich weder das catch noch das finally aufgerufen.  ;D

Gruß Axel