Thursday, December 19, 2013

From Node.js back to Java, Part 1: EventEmitter and Callbacks

There were a lot of features of Node.js and JavaScript that I liked.  Why not bring them to Java?  That's what I'm working on with Nava, "Bringing good ideas from node.js into Java".  So far, I have implemented a fair amount.  The code can be compiled against Java 6, Java 7, and Android 2.2, and presumably later versions of Android.

The emit package is a pretty direct port of a node.js EventEmitter to Java, and can be used in a very similar manner as in node.js.  The main difference is that the event handler cannot be a closure, (cause we don't have closures!) it must implement a simple interface, Emit.IListener, with one method, handleEvent().  Producers of events would subclass or delegate to Emitter, much like the node class would extends or delegate to EventEmitter.  This code essentially replaces Java's EventListenerList and associated classes and interfaces.  There are several major advantages:

  1. The code does not depend on Swing, so it could be used in Android. 
  2. The "events" that get fired need not extend EventObject.  They could be Strings or voids.  However, in many cases, subclassing EventObject makes sense.
  3. Generics are used to obviate the need for a lot of boilerplate, such as declaring a bunch of Listener subclasses and their respective delegation methods addFooListener(), removeFooListener(), etc...
There are also a few disadvantages, noted in the documentation. The main one is that everything is more loosely typed, as you'd expect as this is based upon JavaScript.  Underneath, there are unchecked casts.


The callback package is a rough port of JavaScript / node.js style callbacks.  Your callback "closures" must implement the Callback interface, or extend from AbstractCallback.  Callbacks can be "chained together", either manually using setNextCallback(), or programatically with the utility method Callbacks.chainUp().

You could then "submit" the callback directly by calling the first one, but that would happen synchronously, in-line, and, other than for initial testing, probably isn't worth it.  The more "node-like" technique would be to create a CallbackExecutor (probably application-wide) and use submitCallback(), passing the first callback and it's data.  Your thread will then continue, just like in node, with no need to await the completion (good) and no knowledge of the result (sometimes annoying).

For example, if you had a Reader that read a File into a String, and a Counter that counted words, the skeleton Java code would be:

      Reader reader = new Reader();
      Counter counter = new Counter();
      Callbacks.chainUp(reader, counter );
      CallbackExecutor cex = new CallbackExecutor(2,1);
      cex.submitCallback(reader, theFile);
      // your code then moves on (or just stops...)

As opposed to actual JavaScript/Node, which would look something like:

      fs.readFile(theFile, function(err, data) {
         if (err) throw err;
         // normally the counting code would be in-line here
         // but for modularity similar to the Java
         countWords(data);
       });
       // your code then moves on (or just stops...)


There's more, but that's a start for today.  Check out the readme file, javadocs, and JUnit tests for more information.

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete