XOTS: Background and MultiThreaded Tasks the OpenNTF Domino API Way (Part Three)

Home » XOTS: Background and MultiThreaded Tasks the OpenNTF Domino API Way (Part Three)

In the previous two parts I covered the background of XOTS, using a basic tasklet to return a result, and using a basic tasklet to run a background task allowing the user to continue working with the application while the background task completed.

In that example the background task did not take a huge amount of time, so it might be worth waiting for the task to complete. But it was iterating over all the states and getting a list of names for contacts in those states. Wouldn’t it be more efficient if we processed different states simultaneously, so making the tasklet multi-threaded?

First, let’s look at the tasklet, because that’s not too different from what we’ve seen before.

  1.   @Tasklet(session = Tasklet.Session.CLONE)
  2.   private static class UserLookup implements Callable<String> {
  3.     private String state_;
  4.     private String stateName_;
  5.     public UserLookup(String state, String stateName) {
  6.       super();
  7.       state_ = state;
  8.       stateName_ = stateName;
  9.     }
  10.     public String call() {
  11.       try {
  12.         StringBuilder sb = new StringBuilder();
  13.         View people = Factory.getSession(SessionType.CURRENT).getCurrentDatabase()
  14.             .getView(“AllContactsByState”);
  15.         System.out.println(“Processing “ state_);
  16.         sb.append(stateName_ “: “);
  17.         for (Document doc : people.getAllDocumentsByKey(state_, true)) {
  18.           String name = doc.getItemValueString(“FirstName”) ” “ doc.getItemValueString(“LastName”);
  19.           System.out.println(“Adding “ name);
  20.           sb.append(name);
  21.           sb.append(“, “);
  22.         }
  23.         return sb.substring(0, sb.length() 2) “<br/>”;
  24.       } catch (Throwable t) {
  25.         t.printStackTrace();
  26.         return t.getMessage();
  27.       }
  28.     }
  29.   }

Because we want to wait for the outcome, in line 2 we implement Callable<String>, so the call() method should return a String. We’re not passing in a scoped variable, because we’re waiting for the result and then writing it to the scoped variable. But we do need to state name (for the output) and state abbreviation (for the lookup), so the constructor on lines 6 – 10 expects those variables and maps them to properties of the tasklet, so the call() method can use them. The code in the call() method itself is very straightforward and basically the same as the inner for loop from the background tasklet.

The code for calling it will need a little bit more explanation, but again is not rocket science.

  1. HashMap<String, String> states = new HashMap<String, String>();
  2.       View allStates = Factory.getSession(SessionType.CURRENT).getCurrentDatabase().getView(“AllStates”);
  3.       // Get first five states
  4.       for (Document doc : allStates.getAllDocuments()) {
  5.         states.put(doc.getItemValueString(“Key”), doc.getItemValueString(“Name”));
  6.       }
  7.       // Create a callable for each State
  8.       List<Future<String>> results = new ArrayList<Future<String>>();
  9.       for (String key : states.keySet()) {
  10.         UserLookup getUsers = new UserLookup(key, states.get(key));
  11.         results.add(Xots.getService().submit(getUsers));
  12.       }
  13.       // Now loop through the Xots tasks, and get the results
  14.       TreeSet<String> output = new TreeSet<String>();
  15.       for (Future<String> f : results) {
  16.         output.add(f.get());
  17.       }
  18.       ExtLibUtil.getViewScope().put(“MessageFromIterableXots”, output);

In lines 1 – 6 we iterate through the view of states and create a HashMap for the state abbreviation and the state name.

Then, because we want to wait for the result of the tasklet, in lines 9 – 13 we again create a List of Futures. Again, the datatype expected for the Future is String, because that’s what is being returned by the tasklet. We iterate through the HashMap we created, create a new instance of UserLookup for each state and add it to the List of Futures.

The XOTS service has an invokeAll() method which can take a Collection of Callables. At the moment that’s not working, so we just need to submit each in turn.

Next, in lines 16 – 19 we create a new TreeSet, iterate through the List of Futures and add the outcome of each tasklet, the list of names for that state. The reason for using a TreeSet here is that the tasklets will not complete in order, so e.g. American Samoa might complete before Arkansas. A TreeSet can order its contents, by default alphabetically (case sensitive). So even though the output from each tasklet may not be added in order, the resulting TreeSet is. Finally, on line 20, we add it to the viewScope variable.

Processing multiple threads against a single database and view may not provide significant performance benefits. But if those threads are processing multiple databases, for example, the benefits get bigger.

Currently XOTS is set to run 10 threads at any one time, but that can be configured.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top