This is a followup post to yesterdays post titled Want to join me in building a better Notes Java API? and here I’ll show just how easy it was to build the wrapper API.
Below is the code from the implementation of the getView(String) method of the lotus.domino.Database interface. As you can see the central element is to get the current thread the method is called on and the thread used for Notes data access. If we’re not on the Notes thread execute a blocking request and return the result. If we’re on the Notes thread simply go ahead and do the operation. Simple right?
The most difficult part of all this was that I had to write my own Notes thread handler (equivalent of NotesPlatform) as there isn’t a method for doing a blocking request on the Notes thread in NotesPlatform class supplied by Lotus.
public View getView(final String name) throws NotesException { // get threads Thread curThread = Thread.currentThread(); Thread notesThread = NotesPlatform.getInstance().getThread(); // decide if we're in the Notes thread or not if (curThread != notesThread) { // we're not on the Notes thread so wrap final Holder h = new Holder(); NotesPlatform.getInstance().syncExec(new Runnable() { public void run() { try { h.value = DatabaseWrapper.this.db.getView(name); } catch (NotesException e) { h.throwable = e; } } }); if (null != h.throwable) { throw (NotesException)h.throwable; } else { return new ViewWrapper((View)h.value); } } else { // we're on the thread so do request return new ViewWrapper(DatabaseWrapper.this.db.getView(name)); } }
We’re using something like that blocking technique in the MindPlan application we develop for Haus Weilgut.
That way we don’t have to care much about threading, because most of the Notes accessing code is blocking. We also do a few simple Notes calls from the Swing thread (like getting an INI variable), but most of them in our own Job framework, something like the one in Eclipse for background job handling and synchronization (including scheduling rules to define that some background jobs can/cannot run in parallel.
In MindPlan, we developed the synchronization and blocking on our own.
For another customer, we recently built something based on Executor and Callable from the Concurrency framework of Java.
We have created a “NotesExecutor” that gets called with Callable implementations like
String iniVar=NotesExecutor.execute(new GetIniVariableCallable(“Directory”,true)).get();
The .execute(Callable<?>) method returns a Future<String> object in this case, which has a blocking .get() method.
That way you can wait for the result if you want to or ignore it by not calling .get().
LikeLike
BTW: The NotesPlatform class is not really best practise, because it provides a shared session object across plugins. If one of the plugins calls .recycle() on one container object like a mail database or the whole session, the other plugins also using that session / database crash with "Object has been removed or deleted" or something like that, because Notes internally uses the same C pointer for n Java objects returned in a session.
The best solution is to create a new session for every plugin, and for long-running plugins also for every Notes data access and dispose it afterwards. That way it’s also easier to handle users switching the location/Notes ID, because your code is not full of stored/cached Notes objects that belong to a specific user context (I regret that I did not think more about this a few years ago 😉 ).
Always creating a new Notes session correctly handles switched Notes IDs and is best for memory management.
LikeLike
I agree 100%… 🙂
LikeLike