wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

By Date: September 2016

Domino meets RXJava


Verse on premises (VoP) is nearing its second beta release and fellow Notes experts are wondering if they need to install Apache Solr as part of the VoP deployment. There was a lengthy, high quality discussion and quite some effort evaluating alternatives. In conclusion it was decided to deliver the subset of Solr capabilities needed for VoP as series of OSGi plugins to the Domino server. The project was formed out of the experience with ProjectCastle, which continues as Project OrangeBox to deliver these plugins. In VoP you might encounter one or the other reference to PoB, so now you know where it comes from.
One of the design challenges to solve was to emulate the facet results of the Solr search engine. I build some prototypes and finally settled on the use of RxJava.
RxJava is a member of the ReactiveX programming family, which is designed around the Observer pattern, iterators and functional programming. Check out the main site to get into the groove.
The task at hand is to convert something Domino (a ViewNavigator, a DocumentCollection or a Document) into something that emits subscribable events. I started with turning a document into an NotesItem emitter. Purpose of this was the creation of lighweight Java objects that contain the items I'm interested in. Since Domino's Java has special needs and I couldn't use the ODA, special precaution was needed.
There are plenty of methods to create an Observable and on first look Create looks most promising, but it left the question of recycling open. Luckily there is the Using method that creates a companion object that lives along the Observable and gets explicitly called when the Observable is done. To create the NotesItem emitting Observable I settled on the From method with an Iterable as source. The moving parts I had to create were class DocumentSource implements Iterable<Item> and class ItemIterator implements Iterator<Item>
Why Reactive? In a nutshell: a source emits data and any number of subscribers can subscribe to. Between the emission and subscription any number of filters, modifiers and aggregators can manipulate the data emitted. Since each of them lives in its own little class, testing and composition become very easy. Let's look at an example:
docSource.getItemStream(session).filter(nameFilter).map(toPobItem).map(nameMapper).subscribe(new ItemAdder());
You almost can read this aloud: " The source emits a stream of items, they get filtered by Name, then converted into another Java object (PobItem) and renamed before added to the subscriber.". In a different case you might want to collect all entities (users, groups, roles) that have access to a document, you migh create a "readerAuthorFilter". The individual classes are very easy to test. E.g. the Name filter looks like this:
// requiredFields is is a Collection<String> of NotesItem names to include or exclude
ItemNameFilter nameFilter = new ItemNameFilter(requiredFields, ItemNameFilter.FilterMode.INCLUDE);

public class ItemNameFilter implements Func1<Item, Boolean> {

    public enum FilterMode {
        INCLUDE, EXCLUDE;
    }

    private final Logger      logger      = Logger.getLogger(this.getClass().getName());
    private final Set<String> itemNameSet = new HashSet<String>();
    private final FilterMode  filterMode;

    /**
     * Flexible include or exclude
     * 
     * @param itemNames
     *            Collection of Names to include or exclude
     * @param filterMode
     *            INCLUDE or EXCLUDE
     */
    public ItemNameFilter(Collection<String> itemNames, FilterMode filterMode) {
        this.filterMode = filterMode;
        this.updateItemNames(itemNames);
    }

    public ItemNameFilter(Collection<String> itemNames) {
        this.filterMode = FilterMode.INCLUDE;
        this.updateItemNames(itemNames);
    }

    private void updateItemNames(Collection<String> itemNames) {
        this.itemNameSet.addAll(itemNames);
    }

    @Override
    public Boolean call(Item incomingItem) {
        // Include unless proven otherwise
        boolean result = true;
        try {
            String itemName = incomingItem.getName();
            boolean inList = this.itemNameSet.contains(itemName.toLowerCase());
            result = (inList && this.filterMode.equals(FilterMode.INCLUDE));
        } catch (NotesException e) {
            this.logger.log(Level.SEVERE, e);
            result = false;
        }
        return result;
    }
}


Read more

Posted by on 13 September 2016 | Comments (1) | categories: IBM Notes