Background
I was recently perusing the Notes 6 forum over at the Lotus Developer Domain, as is my wont, and I came across a post entitled, Database access from another class - Java. Now, normally, I would never post a "complete solution" as I did with this thread, but it piqued my interest. It took me a little time in terms of coding, experimenting, and researching Java in Domino, so I thought I'd summarise what I learned in a wee article.
Domino & Java
Whilst I've tinkered a fair bit with Java, one thing I don't have much experience of is creating Java agents. The nearest I got to a Java agent "in production" was a simple implementation of part of the Trackback specification. I did this for fun, but found no further use for it, and let the code languish. So this post on the LDD gave me a chance to "flex my muscles": coding the trackback thing was all "back-end" — Java with no user interface to speak of — whereas this little thing required a front-end that would interact with the agent calling it.
The code & the problem
The original poster in the thread wanted to invoke a simple GUI from a Java agent, collect user input via this GUI, and then do stuff back in the agent. He was using Java because he had a dependency in his code in the form of the JavaMail API. The issue he was experiencing concerned the separation of code into two classes (the main Java agent plus a simple AWT-based class) in that the two weren't talking to each other.
My starting point was to take the code he had, dump it into a dummy database, and get cracking. I called the main agent "DomGUIJavaAgent" and the class it uses for the GUI, "DomGUI". The poster had complained that his code didn't work on two fronts. First, his ViewEntryCollection object came up with inconsistent entry counts. That was easily solved, in that his for loop wasn't properly constructed. The rest of it was harder though...
The problem was that the standard NotesMain method happily instantiated a Database object and all the rest, but couldn't seem to pass these objects over to the default constructor of the GUI class. This meant a lot of head-scratching for me, although I knew it just had to be related to scope. Clearly, the Session and AgentContext classes were key: if they "died" *, then any objects derived with their help weren't going to survive either. I pondered this, and tinkered with different ways of somehow creating "new" AgentContext/ Session objects in the GUI code: not good. Eventually, my sorry excuse for a brain had an epiphany: we're getting into threads!
* - the Domino Java classes and interfaces are "wrappers" around core C functionality in Notes. As such, standard Java garbage collection doesn't work. A good Java coder needs to explicitly "recycle" Document objects and so forth. I gather that the exceptions to this rule are AgentContext and Session. For the sake of brevity in the source code, all recycle calls have been omitted.
Yup, threads
I've always been a bit leery of threads. They're one of those things, along with bit-signing and inner classes, that I seem to have a bit of blind spot with (getting better though!). Now, as I looked more and more into the Domino Java implementation, I discovered that AgentBase, the base class for all Java agents, is a sub-class of the NotesThread class, which makes sense. So, returning to our code, how about running the second GUI object in its own thread? That should work. But we still need to keep the "trigger" — the AgentBase object — alive.
They key to this is getting one thread to "sleep" until the other one is done. So here's my implementation:
In the constructor for the GUI object, I pass through a reference to the Database object we're playing with. The key thing though, is that the method in the DomGUI class that uses this Database object fires off in its own thread and that the Java agent's NotesMain method waits for it. This waiting is accomplished by making the Java agent thread "sleep" for as long as a public boolean variable, belonging to DomGUI, is set to "false." Whilst the thread is alive DomGUI can happily work with the passed-through Database object. Only two things can change the "isClosed" variable to "true": once the relevant button has been pressed or when the window is closed by the user. Splendid.
So, here's the actual agent code in full (it's very brief):
public class DomGUIJavaAgent extends AgentBase {
private AgentContext ac;
private Database db;
private DomGUI myWin;
public void NotesMain() {
try {
ac = session.getAgentContext();
db = ac.getCurrentDatabase();
myWin = new DomGUI(db);
while (!myWin.isClosed) {
Thread.sleep(250);
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
And here's a snippet of the action listener method in the DomGUI class which manipulates "isClosed", the crucial public boolean which holds this all together (sendToAll() is a simple demo method, as you'll see in the full source):
public void actionPerformed (ActionEvent e) {
if (e.getSource() == btnSend) {
sendToAll();
dispose();
isClosed = true;
}
}
The threads
If you look at the full source, you'll notice that a method in the DomGUI code (sendToAll()) initialises its own thread. This thread isn't terminated until the whole window is closed.
Summary
I hope this is useful to any beginning Java coders out there like me... it's the fruit of a fair bit of playing, testing, and actually reading the Domino Designer help...
. Hopefully, it will act as a good starting point for any Domino-based Java you need to write that requires a UI. I appreciate that this isn't the prettiest GUI, but it's really just to show what can be done... and it's also R5-compliant!My thanks to Joseph Millar of Brightline Technology for making a really useful observation concerning thread termination which I took on board in this code. Additional comments from those more experienced than I are more than welcome!
Attachment(s): file/DomGUIJavaAgent.java]DomGUIJavaAgent.java (0 KB) file/DomGUI.java]DomGUI.java (4 KB)