Hi alle zusammen,
ich habe die Ehre einen Java-Agenten weiterzuentwickeln, den ein ehemaliger Kollege (Gruß an Magi_Halli) damals entwickelt hat.
Vorweg:
Meine Notes-Kenntnisse sind eher "beschränkt" und beruhen auf Google&Co. + Learning by Doing.
Wir benutzen die 8.5er Version.
Es ist ein reiner Java-Agent, der als JAR Archiv komplett per "Projekt bearbeiten" deployed wird.
Er extended NICHT die AgentBase oder irgendwelche anderen Notes Klassen. Reines simples Java-Proggi also. (wird per LotusScript & LS2J aus dem Notes aufgerufen)
Soweit funktioniert im Grunde auch alles wunderbar - wenn auch relative träge.
Nur bin ich gestern auf den Trichter gekommen die GUI des Agenten zu überarbeiten, welche initial nur aus JDialogs bestand. Da diese aber seltsamerweise nicht immer in den Vordergrund kommen, wollt ich aus der Haupt-Gui-Klasse einen JFrame machen, der wenigstens als Fenster in der Windows Taskbar erscheint und somit anklickbar ist.
Gesagt getan, alles refactored und klappt im Eclipse alles wunderbar.
Im Notes jedoch kommt beim Aufruf zwar noch mein JFrame nach oben, wenn ich jedoch auf einen Button klicke, um einen JDialog (Suchdialog) anzuzeigen, erscheint dieser nicht mehr!
In der DebugConsole konnt ich rauskriegen, dass er die benötigten Klassen nicht finden konnte und deshalb dann NullPointers geschmissen hat. Nach einigen Recherchen und Ergebnissen meines Ex-Kolleges habe ich rausgefunden, dass ich mit einer zuvor erstellten Instanz der jeweils angemeckerten Klasse den Fehler umgehen kann...
....okay, so viel sinds im Grunde nicht... einfach mal vorher von allen eigenen & für die Aktion benötigten Klassen eine sinnlose Instanz erstellt. Tja und dann geht's halt irgendwann in die java.awt Klassen runter --- und da hört der Spaß auf. >:(
Hier die Exception:
2009-12-09 10:48:37.019 [de.ibees.notes.addin.gui.AddInMainFrame] ClassLoader of de.ibees.notes.addin.gui.AddInMainFrame is: lotus.domino.JavaConnectLoader
2009-12-09 10:48:44.238 [de.ibees.notes.addin.MailHandler] java.lang.NullPointerException
at lotus.domino.JavaConnectLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(ClassLoader.java:643)
at java.lang.ClassLoader.loadClass(ClassLoader.java:609)
at lotus.domino.JavaConnectLoader.loadClass(Unknown Source)
at de.ibees.notes.addin.gui.SuchDialog.initialize(SuchDialog.java:114)
at de.ibees.notes.addin.gui.SuchDialog.<init>(SuchDialog.java:101)
at de.ibees.notes.addin.search.SuchHandler.<init>(SuchHandler.java:95)
at de.ibees.notes.addin.MailHandler.buttonAPartner_actionPerformed(MailHandler.java:372)
at de.ibees.notes.addin.MailHandler.actionPerformed(MailHandler.java:289)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:2008)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2331)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:400)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:255)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:249)
at java.awt.Component.processMouseEvent(Component.java:6054)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3278)
at java.awt.Component.processEvent(Component.java:5819)
at java.awt.Container.processEvent(Container.java:2071)
at java.awt.Component.dispatchEventImpl(Component.java:4426)
at java.awt.Container.dispatchEventImpl(Container.java:2129)
at java.awt.Component.dispatchEvent(Component.java:4256)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4335)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3999)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3929)
at java.awt.Container.dispatchEventImpl(Container.java:2115)
at java.awt.Window.dispatchEventImpl(Window.java:2453)
at java.awt.Component.dispatchEvent(Component.java:4256)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:612)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:286)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:196)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:186)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:181)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:173)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:134)
die besagte Zeile 114 ( this.addWindowListener(new java.awt.event.WindowAdapter() {) des SuchDialoges macht im Grunde nur die simplen Initialisierungen für den Dialog:
Auszug Suchdialog:
this.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
this.setLocation(new Point(180, 180));
this.setResizable(true);
this.setModal(true); // this wartet auf Ein-/Ausgabe, bevor close
this.addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
getButtonAbbrechen().doClick();
}
});
Auszug MailHandler (wo der ganze Spaß starten)
public void buttonAPartner_actionPerformed(ActionEvent e) {
try {
// Aufruf (+ Auswahl zurückgeben):
sucheHandler = new SuchHandler(APARTNER_ID);
Apartner ap = ((Apartner) sucheHandler.getSelectedAPartner());
if (sucheHandler.isConfirmStatus() == true && ap != null) {
// comboApartner neu füllen
comboApartner_fillBySearch();
}
else {
// Nothing
NotesAddInLogger.info(this.getClass(), "Abbruch in AP-Suche!");
}
}
catch (Exception err) {
NotesAddInLogger.error(this.getClass(), err);
}
}
Der Witz ist, wenn ich die Hauptklasse "AddInMainFrame" wieder von extends JFrame auf extends JDialog umwandle geht der ganze Spaß wieder wunderbar.... es liegt also DEFINITIV am JFrame.
So und nun bin ich mal gespannt, ob mir da einer was erklären kann!??!?!
Irgendwie scheint er die Klassen nicht zu finden, wenn ich vom JFrame aus den Dialog anzeigen will!?!? Nur warum klappt das, wenn die Ursprungsklasse nen Dialog ist?
Ein deployen ins jvm/lib/ext Verzeichnis fällt flach, da ich dann sicherlich Probleme kriege mit dem Deployen auf den Clients, was er so über "Gestaltung aktualisieren" wunderbar schafft.
Hoffe jemand hat nen Tipp
Grüße aus Dresden
Christian
Hallo Ralf,
danke für die ausführliche Antwort.
Du hattest recht, die Endlosschleife verhindert erstmal das Rückspringen und meine Dialoge kommen wieder hoch.... aber dafür ist nun die CPU bei 100%...
Hier mal der Code:
mainframe.setVisible(true);
while(mainframe.isVisible()){
// Dummy Schleife
}
NotesAddInLogger.debug(this.getClass(), "Nach der EndlosSchleife!");
Anhand der Log seh ich, dass er wirklich erst beim Beenden des Frames diese Schleife verlässt. Aber dafür hängt halt der Rechner mit Volllast.
Wenn ich nun einen Extra Thread mache, der nur "kreiselt" solange der Frame sichtbar ist, kommt es sicherlich auf's gleiche raus wie vorher oder? (der AWT Thread ist beendet und gibt die Steuerung an das LS2J zurück, oder?)
Meine Idee nun, ich generiere einen JDialog, welcher unsichtbar irgendwo im Hintergrund ist und aus dessen Konstruktor erzeug ich meinen Frame (wenn das denn geht, noch nie gemacht). Beim Schliessen des Frames, schliess ich auch meinen BackgroundDialog und vielleicht klappt's dann.
Würde denn ein eigener Classloader weiterhelfen?
Grüße aus DD
Christian
Hallo Ralf,
hab es mal mit einem DummyThread probiert... funktioniert einwandfrei - auch in Betracht auf die CPU Last. Hier mal die Klasse:
package de.ibees.notes.addin.tools;
import de.ibees.notes.addin.gui.AddInMainFrame;
public class LS2JDummyWaitThread extends Thread {
private static AddInMainFrame frameInstance = null;
public LS2JDummyWaitThread(AddInMainFrame frame){
if (frame == null){
throw new IllegalArgumentException("Frame darf nicht <null> sein!");
}
NotesAddInLogger.debug(this.getClass(), "Neuen WarteThread erzeugt!");
frameInstance = frame;
}
public void run(){
while (frameInstance.isVisible()) {
if (!this.isInterrupted()){
try {
// NÜSCHT Machen, nur kreiseln.... !
NotesAddInLogger.debug(this.getClass(), "Muss warten...");
this.sleep(10000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
NotesAddInLogger.debug(this.getClass(), "WarteThread abgebrochen...");
break;
}
}else{
NotesAddInLogger.debug(this.getClass(), "WarteThread interrupted...");
break;
}
}
}
}
diesen Thread starte ich nach dem setVisible(true) des MainFrames
mainframe.setVisible(true);
dummyThread = new LS2JDummyWaitThread(mainframe);
dummyThread.start();
dummyThread.join();
NotesAddInLogger.debug(this.getClass(), "Ende in MailHandler.initialize() Methode!");
Bei Ok oder Abbrechen wird der dummyThred einfach interrupted.
Vom Prinzip her.
Im Notes soll der User eine Mail in einem Ordner markieren. Über einen Button in der Toolbar wird diese Mail per LotusScript auseinandergenommen (Absender/Body/Attachments etc).... danach wird der Java-Agent gestartet (wie schon initial erwähnt, (noch) keiner der irgendwelche Notes-Klassen erweitert). Der Agent kontaktiert unseren ERP-Server welcher je nach Daten die Kontaktdaten zu dem Absender liefert. Noch paar Userinteraktionen abfragt (Projektbezug, Bezüge, TicketNr generiert und und und... -> teilweise auswählbar aus ComboBoxes etc. oder aber auch suchbar über Suchdialoge (und da klemmte es ja nach Umbau auf JFrame)).
Wird im Agenten nun auf OK geklickert, so wird die Mail quasi ins ERP übertragen und erhält im Notes eine Eingangsnummer (separates Feld).
Das war's auch schon.
Ich will das Ding irgendwann mal als reinen Notes-Agenten implementieren, so dass ich die LotusScript Krücke vorher weglassen kann, aber das wird wohl etwas aufwendig, da ich dies nicht selbst implementiert hab (Gruß an Magic_Halli) und ich so erstmal den LotusScriptCode verstehen muss. :-)
In dem Sinne
Danke für die Zuarbeit, wenn Du noch Verbesserungsvorschläge hast, bin ich natürlich ganz Ohr
Christian