Lotus Notes / Domino Sonstiges > Java und .NET mit Notes/Domino
Java Agent "Error cleaning up agent threads"
machineslave:
Hallo,
Ich habe einen Java-Agenten, der über JDBC Daten aus einer DB2 liest und in einer Notes-DB speichert. Der Agent läuft einwandfrei durch, allerdings bekomme ich auf der Konsole die Meldung "Error cleaning up agent threads". Wenn ich den Agenten des öfteren laufen lasse, so bekomme ich irgendwann eine Out of Memory Fehlermeldung.
Ich habe auch schon die ganze Thematik bzgl. recycle() eingebaut, aber irgendwie komme ich da nicht weiter.
Hier mal eine (verkürzte) Version meiner Java-Klassen:
import lotus.domino.*;
import java.sql.*;
public class ImportAS400Data extends AgentBase {
Log log;
Session session;
Database db;
AgentContext agentContext;
Document styleDoc;
String username, system, password, library, param;
public void NotesMain() {
try {
Session session = getSession();
AgentContext agentContext = session.getAgentContext();
Log log = session.createLog("Photoalbum");
log.openNotesLog("HierStehtDerServername", "develop\log.nsf");
db = agentContext.getCurrentDatabase();
View configView = db.getView("Configuration");
View activeJobView = db.getView("Active Job");
View inboxView = db.getView("($Inbox)");
Document activeDoc = activeJobView.getFirstDocument();
Document as400Doc = configView.getDocumentByKey("AS400");
system = as400Doc.getItemValueString("AS400System");
username = as400Doc.getItemValueString("AS400User");
password = as400Doc.getItemValueString("AS400Password");
library = as400Doc.getItemValueString("AS400Library");
if (activeDoc == null) // Kein aktiver Job
{
activeDoc = inboxView.getFirstDocument();
if (activeDoc != null) // Neues Dokument gefunden
{
// Dokument in Job queue und ersten Agenten starten
activeDoc.removeFromFolder("($Inbox)");
activeDoc.putInFolder("Active Job");
activeDoc.replaceItemValue("Agent", "imp_clr");
activeDoc.replaceItemValue("Status", "active");
activeDoc.save(true, false);
param = activeDoc.getItemValueString("Subject");
log.logAction("Importing colors from AS/400: " + param);
ImportColors colors = new ImportColors(system, username, password, library, db, param);
log.logAction("Imported " + colors.getRecords() + " color records");
activeDoc.replaceItemValue("Status", "completed");
activeDoc.save(true, false);
activeDoc.recycle();
colors = null;
}
}
log.recycle();
configView.recycle();
activeJobView.recycle();
inboxView.recycle();
as400Doc.recycle();
db.recycle();
} catch (NotesException e1) {
e1.printStackTrace();
}
System.gc();
}
}
import java.sql.*;
import lotus.domino.AgentBase;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.NotesException;
public class ImportColors extends AgentBase{
public int records;
public ImportColors (String system, String username, String password, String collectionName, Database db, String param) {
try {
DriverManager
.registerDriver(new com.ibm.as400.access.AS400JDBCDriver());
Connection connection = DriverManager.getConnection("jdbc:as400://"
+ system, username, password);
DatabaseMetaData dmd = connection.getMetaData();
// Execute the query.
Statement select = connection.createStatement();
String tableName = "SGEEANP";
ResultSet rs = select.executeQuery("SELECT * FROM "
+ collectionName + dmd.getCatalogSeparator() + tableName + " WHERE SEPARM='" + param + "'");
int x = 0;
while (rs.next()) {
try {
Document doc = db.createDocument();
doc.replaceItemValue("Form", "SGSCLRP");
doc.replaceItemValue("SFSSEA", rs.getString("SESSEA"));
doc.replaceItemValue("SFYEAR", rs.getString("SEYEAR"));
doc.replaceItemValue("SFDIV", rs.getString("SEDIV"));
doc.replaceItemValue("SFSTYL", rs.getString("SESTYL"));
doc.replaceItemValue("SFSCLR", rs.getString("SESCLR"));
doc.replaceItemValue("SFCDSC", rs.getString("SECDSC"));
doc.replaceItemValue("param", param);
doc.save(true, false);
x = x + 1;
} catch (NotesException e2) {
e2.printStackTrace();
}
}
setRecords(x);
rs.close();
select.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Ich hoffe, hier gibt es ein paar Notes/Java-Cracks
Gruß
Stefan
flaite:
1. Du schliesst zwar die dein Statement-Objekt und dein Resultset. Das eigentlich wichtige (connection) aber nicht. Mach am Ende connection.close(); Bei moderneren Treibern werden Statement und Resultset mitgeschlossen.
Hab das lange nicht mehr mit Domino gemacht. Es kann sein, dass du den DriverManager wg. irgendwelchen JDBC-Notes Security kommen sich in die Quere issues anders instantiieren mußt. Bin mir aber nicht so sicher, ob das noch gilt. Rose Kelleher hat damals darüber geschrieben.
( ;D : Das ist möglicherweise der originale Thread, in dem das Problem erstmals besprochen wurde:
http://www-10.lotus.com/ldd/46forum.nsf/0/9D47C1F9E82455F785256614001FDFF6?OpenDocument )
2. VERWENDE PreparedStatement und nicht Statement. Die Art wie du die Statements zusammenbastelst ist unsicher. Jemand könnte vielleicht sowas in das Feld param schreiben:
--- Code: ---und tschüss"; delete from veryImportantTable
--- Ende Code ---
Mit PreparedStatement kann sowas nicht passieren.
Mach dir PreparedStatement zu einer Gewohnheit. Ist nicht so schwierig.
http://java.sun.com/docs/books/tutorial/jdbc/basics/prepared.html
3. Du solltest mit try-catch-try-catch arbeiten, wenn du schon mit raw JDBC hantierst (mit Notes gehen glaub ich andere Sachen gar nicht).
Bsp:
--- Code: ---Connection connection = null;
try {
connection = deinZeugs
[...]
connection.close();
connection = null;
}
catch (SQLException sqle) {
try {
if (connection != null) connection.close();
} catch (SQLException sqle) {}
--- Ende Code ---
Wenn in dem Bereich wo die 3 Punkte sind ein Fehler auftritt, bleibt die Connection offen. Das ist nicht so gut.
Im übrigen könntest du dein SQLException Handling verbessern. Aber das schüttel ich auch nicht so einfach aus dem Ärmel. In SQLException stecken eine Menge weiterer Informationen wie allgemeiner oder Datenbankspezifischer SQL-Errorcode etc.
Denke, dass sich da noch einiges optimieren lässt.
Warum generierst du pro Dokument einen Remote Call gegen die DB?
In SQL gibt es sowas wie
--- Code: ---where wert in (a, b, c, d, e);
--- Ende Code ---
Es dürfte performanter sein, dir ein Resultset zu holen und damit zu arbeiten.
oder so ähnlich.
Ralf_M_Petter:
Zusätzlich zu den Sachen die Axel bereits angeführt hat ist mir noch aufgefallen, dass die ImportColors von AgentBase abgeleitet ist, was in diesem Fall falsch ist, da das kein eigener Agent. Du solltest diese Klasse meiner Meinung nach von Object ableiten. Weiters aufgrund von deinem Source gehe ich davon aus, dass du das auf einer AS/400 (I/5) laufen lässt. Ich bevorzuge in diesem Umfeld eigentlich meistens dass man den Java Code extern in der JVM von der I/5 laufen lässt und über den Job Scheduler aufruft. Dann kann man nämlich auch neuere JDK Version bis 1.5 (5.0) verwenden. Das läuft wirklich sehr gu Das geht natürlich nicht, wenn du den Agenten einen anderen Trigger als Zeitplan hast.
Dein Code ist auch ziemlich unvollständig, es fehlen teilweise Methoden wie colors.getRecords(). Gehe aber mal davon aus, dass du das gerade hier herausgenommen hast. Was mir weiter nicht besonders gut gefällt ist, dass du für jeden Durchlauf eine neue Connection aufbaust. Ich würde die Connection zur DB/2 einmal aufbauen, dass SQL Statement Preparen und die Performance wird extrem besser sein, da vor allem bei der DB/2 auf der I/5 ein Prepare eine sehr teure Operation ist. Ebenso natürlich das Connecten.
Übrigens in der While Schleife in ImportColors fehlt das recycle des Dokuments bei jedem Durchlauf. Keine Ahnung wie oft die Schleife durchlaufen werden kann, aber das kann schon zu einen Out of Memory führen.
Dafür kannst du dir aber die vielen Recycles in deiner Main sparen. Ein Recyceln der Datenbank recycelt auch alle abhängigen Objekte.
Falls du noch Fragen hast, gerne.
Grüße
Ralf
flaite:
--- Zitat von: Ralf_M_Petter am 19.12.05 - 18:27:32 ---Ich würde die Connection zur DB/2 einmal aufbauen, dass SQL Statement Preparen und die Performance wird extrem besser sein, da vor allem bei der DB/2 auf der I/5 ein Prepare eine sehr teure Operation ist. Ebenso natürlich das Connecten.
--- Ende Zitat ---
Geht natürlich auch. Wobei man - wie bereits gesagt - PreparedStatements schon allein aus Security Gründen benutzt (kann nicht oft genug gesagt werden).
Ich bin übrigens der Meinung, dass man mit einem Resultset auskommt und das dies noch performanter ist. Einfach den oben angesprochenen in (werta, wertb, ...) benutzen und gegebenenfalls die wichtigen Werte der NotesDocumentCollection in einer HashMap zwischenspeichern.
Vielleicht fehlt auch DriverManager.deregisterDriver()
machineslave:
Hallo,
also ich habe mal connection.close() und DriverManager.deregisterDriver() eingebaut.
Jetzt bekomme ich direkt beim zweiten Aufruf des Agenten folgenden Fehler:
AMgr: Agent ('ImportMonitor' in 'stefan/photoalbumImportInterface.nsf') error message: Exception
12/20/2005 15:34:40 AMgr: Agent ('ImportMonitor' in 'stefan/photoalbumImportInterface.nsf') error message: in thread "main"
12/20/2005 15:34:40 AMgr: Agent ('ImportMonitor' in 'stefan/photoalbumImportInterface.nsf') error message: java.lang.OutOfMemoryError
12/20/2005 15:34:40 AMgr: Agent ('ImportMonitor' in 'stefan/photoalbumImportInterface.nsf') error message: <<no stack trace available>>
12/20/2005 15:34:40 JVM: The addAttachment (Ljava/lang/String;[B)V() method failed.
Das merkwürdige an der ganzen Geschichte ist, dass der Agent komplett auf dem Client durchläuft, ohne zu mucken.
Navigation
[0] Themen-Index
[#] Nächste Seite
Zur normalen Ansicht wechseln