Wednesday, July 21, 2010

Using Rhino Javascript with Java

Javascript, Rhino and Java

This article looks at

  • installing rhino
  • using rhino to extend interfaces and abstract classes
  • part of the motivation for this article is that I want to show in future articles a way to build a repl on top of rhino's shell and use it to explore java technologies such as neo4j in an interactive and exploratory way
  • this article is just covering the basics

I recently got into rhino and I've found it to be not only a lot of fun but also a useful way to explore the java universe. This is a universe that is populated by such things as neo4j, apache software such as apache derby, lucene, and of course xml technologies such as existdb discussed recently here and webservices and semantic web technologies such as openrdf and so on.

There are many ways to explore this universe without having to write java and besides using rhino. So is there a reason to use rhino over jruby, or clojure or scala etc? I can't give an objective answer, but some points:

  • I'm attracted to javascript because it is a very simple language; a bit like a stripped down version of scheme without the fun stuff but with a far more familiar C-like syntax and, of course, closures.
  • rhino and some other javascript variants also have an interesting extension that makes xml a native datatype; this is not something I'll explore here but makes it an interesting fit for xml-centric java technologies; rhino has e4x built in and ready to go
  • javascript is quite possibly the most well-known language on the planet thanks to its close ties with the web and html
  • javascript can provide a scripting glue for coordinating java code where the latter performs the heavy lifting; this is a familiar paradigm that goes at least as far back as Unix itself with its C libraries and shell interpreters. Hence we can do things like run tests or engage in exploratory programming using rhino overlaid on a java application
    • of course, this last point applies to any language that runs on the jvm such as jruby etc
  • I am not sure if all other languages that run on the jvm can extend an abstract class; this is definitely something that rhino can do

Installing Rhino

  • I recommend installing rhino from the people who made it rather than the version that comes with more recent versions of Sun (Oracle's) java

    • one key difference is that the mozilla version of rhino allows you to extend abstract classes and implement multiple interfaces; the current version from sun allows you to implement only one interface
      • to quote this article:

        "Rhino's JavaAdapter has been removed. JavaAdapter is the feature by which a Java class can be extended by JavaScript and Java interfaces may be implemented by JavaScript. This feature also requires a class generation library. We have replaced Rhino's JavaAdapter with Sun's implementation of the JavaAdapter. In Sun implementation, only a single Java interface may be implemented by a JavaScript object."

  • Of course wanting to do more than implement a java interface is probably rare - to be honest I don't really know. But just for kicks I'm going to show an example of extending an abstract class in this article.

  • I've been using rhino1_7R2 which is the latest version
    • the download page is here
    • you just have to download the zip file and unpack it somewhere
    • once you've unpacked it, you can go in and
      • look at the javadocs
      • look at the source
    • but all you really need is one jar file, js.jar which should be in the root directory; that's all you need to get rhino going (besides a working implementation of java 6)
      • ignore the js-14.jar ; it lacks some features of js.jar (I can't remember what they are)

Setting up in bash (on linux)

  • Here are some bash shell script variables functions for running rhino
    • JAVAOPTS=-Djava.net.preferIPv4Stack=true
      • this is to force java to ipv4; for some reason on my system, it defaults to ipv6 regardless of settings I have made in /etc/java-6-sun; this has nothing to do with rhino and you can safely exclude it if you don't suffer this problem; or use it to pass other options
    • RHINO_CLASSPATH=/home/danb/javascript/rhino/rhino1_7R2
      • this is my path to where I unpacked the rhino zip file which you will need to change to your path for your setup
  • This bash function sets up a basic rhino shell
    js() {
         rlwrap java $JAVAOPTS -cp $RHINO_CLASSPATH/js.jar \
                  org.mozilla.javascript.tools.shell.Main $*
    }
    
  • I usually have the variables and functions like the above in a file that I source into my interactive shell:
    bash> . rhino-utils.sh
    
  • Java recommends you pass your classpaths via the -cp switch to java (rather than using environment CLASSPATH variable)
    • one gotcha is that when including jar files you must specify the name of the jar file, or include a wildcard asterisk; simply saying $RHINO_CLASSPATH by itself is not enough to pick up jar files (only .class files)
  • rlwrap is an optional extra I throw in to give the rhino shell readline capabilities similar to bash.
    • apparently rhino can be run with jline - but this is something I haven't tried yet

Test it

  • Create a file: test.js
    • in it type:
      print('hello world!');
      
  • Run it
    bash> js test.js
    hello world!
    
    • it may take a while for the script to run as java starts up; but you should see "hello world!"
  • You may be wondering where 'print' comes from; it is part of a bunch of rhino utilities that are available to you when running the rhino interpreter

Error messages

  • When you get errors running rhino shell, you may find the error messages unhelpful and distinctly lacking with regards line numbers
  • This can be remedied by adding a "-debug" switch to your invocation

Compiling

  • Rhino can be compiled into java bytecode
  • This will not yield the same type of performance as compiled java however so don't get too excited.
  • Add the following functions to your bash file and re-source it into your shell:
    compile() {
      java $JAVAOPTS -cp $RHINO_CLASSPATH/js.jar \
        org.mozilla.javascript.tools.jsc.Main $*
    }         
    run() {
      java $JAVAOPTS -cp .:$RHINO_CLASSPATH/js.jar $1
    }
    
  • Now compile the previous test.js file
    bash> compile test.js
    
    • this should produce a file called test.class
  • Now run it with
    bash> run test
    hello world!
    

Implementing an interface

  • Java interfaces are one of the most powerful features of the java language; most good java api's will make specific use of them; and rhino makes it extremely easy to implement them.
  • The classic example given is to implement a Runnable interface for a java thread
  • Rhino makes this as easy as instantiating the interface like an object and passing a javascript object literal with the required function definitions as members:
        var Runnable = java.lang.Runnable;
        var Thread = java.lang.Thread;
        var System = java.lang.System;
        var coffee = {run: function() {
                             Thread.sleep(2);
                             for(var i=0;i<10;i++) System.out.println("I like coffee!");} };
        var tea    = {run: function() {
                             for(var i=0;i<10;i++) System.out.println("I like tea!");} };
        var r1 = new Runnable(coffee);
        var r2 = new Runnable(tea);
        var t1 = new Thread(r1);
        var t2 = new Thread(r2);
        t1.start();
        t2.start();
    
    • In the above example we create 2 threads and start them (using the rhino shell)
    • Notes
      • I've delayed the first thread a little
      • we pass coffee and tea (javascript object literals) to Runnable, a java interface
      • we defined the java classes we want to use upfront; in this case: Runnable, Thread and System
        • to be more precise we could have said:
            var Runnable = Packages.java.lang.Runnable;
          
          • "Packages" is a global provided by rhino to access java packages
            • java.* packages (and possibly some others) are made available without having to use "Packages"
          • Be aware that there are other ways to import packages and specific classes into rhino's global namespace such as importPackage function and JavaImporter constructor
  • When we instantiate Runnable (in javascript) what's really happening is this:
     var r1 = new JavaAdaptor(Runnable,coffee);
    
    • rhino is just giving us a nice shorthand for making use of its JavaAdaptor
  • It's worth taking 5 minutes to read this article as it sets out a lot of the basics about how rhino javascript interoperates with its java host

Jetty and java servlets: Extending an abstract class

We are going to create a HelloWorld servlet in rhino.

I am not suggesting rhino should be used to create servlets. This is merely an example I worked on whilst playing with jetty; and information on extending abstract classes (a necessity for servlets, for instance) is rather sparse.

  • jetty is an interesting platform; some of my motivations for looking at it are:
    • support for continuations
    • ability to run multiple containers under one jvm
      • this isn't a special feature; however it is not trivial, for instance, to run several small independent sites on one rails app (a ruby process)
    • hot deployment class loading
    • an apparently good api for asynchronous web service calls
  • documentation for these things is not exactly lavish; these were just points of interest that I noticed and I may even be incorrect on their status;

  • I am going to use the latest jetty, version 7

    • it is part of the eclipse stable
    • the version I ended up downloading was jetty-hightide-7.1.4.v20100610.tar.gz
    • all the jar files you need are in the lib directory
  • First we build the servlet; create a HelloServlet.js file with one method:

        // HellowServlet.js file
    
      function doGet(httpServletRequest, httpServletResponse) {
          httpServletResponse.setContentType("text/plain");
          var out = httpServletResponse.getWriter();
          out.println("Hello World - we're written in javascript!");
          out.close();
      }
    
  • this method will override the doGet method provided by the abstract http servlet class.
  • Now create a second file, launch.js
        // launch.js file
    
      var Server = org.eclipse.jetty.server.Server;
      var SocketConnector = org.eclipse.jetty.server.bio.SocketConnector;
      var ServletHandler = org.eclipse.jetty.servlet.ServletHandler;
      var server,connector,handler;
    
      server = new Server();
      //server.addListener(":8070");  // Not in Jetty 7
    
      connector = new SocketConnector();
      connector.setPort(8080);
    
      //server.setConnectors(new Connector[] { connector });  // java version
      server.setConnectors([ connector ]);  // js version
    
      handler = new ServletHandler();
      server.setHandler(handler);
    
      handler.addServletWithMapping("HelloServlet","/");
    
      server.start();
      server.join();
    
  • notes

    • we're embedding jetty into our rhino app
    • there appear to be some changes between version 6 and 7 of jetty; this code is for version 7
    • I source the java objects I need from jetty into my rhino context at the beginning; these are Server, SocketConnector, ServletHandler.
    • note how the 'setConnectors' call uses a js array rather than a java array
    • this example is based on a minimal embedded example provided in jetty7's source;
      • you may need to download the source separate to the above tarball;
      • once unpacked the example is at:
        • jetty/example-jetty-embedded/src/main/java/org/eclipse/jetty/embedded
    • addServletWithMapping expects a class file; so we will compile our HelloServlet.js servlet above
  • the following is the bash script you could use to first compile and then run jetty using the 2 files above; you will need to modify JAVAOPTS, JETTY_CLASSPATH and RHINO_CLASSPATH:

      JAVAOPTS=-Djava.net.preferIPv4Stack=true
      JETTY_CLASSPATH=/home/danb/javascript/rhino/jetty/jetty/jetty-hightide-7.1.4.v20100610/lib/
      RHINO_CLASSPATH=/home/danb/javascript/rhino/rhino1_7R2
    
    jetty.compile() {
      java $JAVAOPTS -cp .:$JETTY_CLASSPATH/\*:$RHINO_CLASSPATH/js.jar \
        org.mozilla.javascript.tools.jsc.Main \
        -extends javax.servlet.http.HttpServlet \
        HelloServlet.js
    }
    
    jetty.launch() {
      java $JAVAOPTS -cp .:$JETTY_CLASSPATH/\*:$RHINO_CLASSPATH/js.jar \
        org.mozilla.javascript.tools.shell.Main -debug launch.js
    }   
    
  • notes

    • you may need to escape asterisks in your classpaths to prevent the shell from expanding them
    • your shell might not support dots in shell function names, so replace as required
  • compile the servlet

    bash> jetty.compile
    
    • this should produce 2 class files
  • then launch it
    bash> jetty.launch
    
  • head over http://localhost:8080 and you should see
    Hello World - we're written in javascript!
    

Concluding Remarks

Rhino is a lot of fun. If you want to play with some java tech but don't want the verbosity of java, rhino is a very flexible means to do this. Rhino imports the scheme-like simplicity of javascript straight into java along with a shell to help you explore it interactively. I hope to explore the shell and repl features for interacting with java in a later post.

Friday, July 16, 2010

Emacs, mozrepl and javascript autotesting

Emacs, mozrepl and javascript autotesting

This article shows how to use mozrepl in conjunction with emacs to run tests (or some other function) automatically in a web page in your browser as you work on a related javascript file in emacs.

This is not some perfect setup. And given how quickly things change these days it may be hard to replicate. But it shows the power of several different technologies interacting to give us more freedom in what we do and how we do it.

  • To follow this blog post, you need to:
    • install mozrepl
      • mozrepl is a firefox addon; there is an older version called mozlab; you should go here instead though where you should find a link for the .xpi file
      • after restarting firefox, you should be able to telnet to the js repl
          bash> telnet localhost 4242
          ... [header stuff]...
          repl1>
        
        • this is a nice little repl; you can experiment with javascript here and test things out; you might want to check out the tutorial
        • of course, doing it in a telnet program isn't so much fun; you could wrap it in readline library such as:
          bash> rlwrap telnet localhost 4242
          ... [header stuff]...
          repl1>
          
        • this should give you similar functionality that you'd expect on the bash prompt
        • of course, there's arguably a better solution still, which is to run it in emacs since this will allow you to
          • edit javascript files and send parts or all thereof to mozrepl directly, and
          • generate command strings dynamically in emacs and send these directly to mozrepl
      • which leads us to mozrepl.el ...
    • install mozrepl.el in your emacs
      • instructions are here
      • I should note that the javascript mode I'm using in emacs comes from here
      • and my mozrepl.el comes from another emacs mode: nxhtml mode
      • these were some additional setup lines in my .emacs
          (add-to-list 'auto-mode-alist '("\.js$" . js-mode))
          (autoload 'inferior-moz-mode "moz" "MozRepl Inferior Mode" t)
          (autoload 'moz-minor-mode "moz" "MozRepl Minor Mode" t)
          (add-hook 'js-mode-hook 'javascript-moz-setup)
          (defun javascript-moz-setup ()
            (interactive)
            (moz-minor-mode 1))
        
      • at the moment, javascript development in emacs seems to be in a state of flux with several js/javascript mode versions floating around; getting set up can be confusing and is beyond what I am trying to share here
      • you should be able to perform this command in your emacs (or something like it)
        M-x run-mozilla
        

        and have a new buffer open up with a repl prompt similar to the telnet experience above.

Putting it together

My set up has the following features:

  • I use a home-grown test framework called unitjs that allows me to group my tests into sections, and group these sections into modules (functions) that I can run either when the page loads, or on clicking a button (etc)
  • I have an html page for testing that loads my js project files and the testing framework I am using to test the project as well as the tests themselves
  • these tests are not run automatically but sit in functions (which I loosely refer to as modules)
  • for instance, I might have
    var tests = {};
    ...
    tests.module1 = function() {
      ... do some setup ...
      ... define some test functions with assertions ...
      ... call the test runner to run the tests above and collect the results ...
    }
    
  • each module corresponds to a separate js file in the tests folder of my project
    • this is important for the setup I am demonstrating as will be shown shortly
  • to run the tests for module1, for instance, you just need to invoke the function
    tests.module1()
    

    and the results will get displayed on the test page

  • similarly for tests.module2 and so on...

Hooking up emacs, mozrepl and autotesting

What we're going to do now is get emacs to automagically work out which test module file we have just saved and send the command (tests.moduleX()) to mozrepl, which will then execute this command within the context of our test page, thereby running and displaying the tests

  • in your .emacs file we set up a function that does the job of reloading the page and sending the test instructions:

      (defun me/mozrepl/run-after-reload (str)
       "Send string to mozrepl, but only after reloading"
       (interactive)
       (progn
         (comint-send-string (inferior-moz-process) "repl.home();")
         (comint-send-string (inferior-moz-process) "BrowserReload();")
         (sleep-for 0 500)
         (comint-send-string (inferior-moz-process) "repl.enter(content);")
         (comint-send-string
          (inferior-moz-process) str )))
    
  • [18-Jul-2010]: I've added ';' after each js command in the above defun

  • all we're doing here is sending a series of commands via the repl
    • each comint-send-string command is as though we were typing that command into the repl ourselves
  • note that the commands reload the browser page, then get mozrepl to enter into the context of the document (repl.enter(content)); this gives us access to our javascript code that is loaded on this page
    • you can check by typing into the repl the name of one of your global variables in your javascript project; the repl should have access to it and you should be able to use it
    • when I was playing with mozrepl this wasn't at all obvious and I almost gave up trying to access the javascript that had loaded with my page until I discovered this step
  • once we're in the correct context, we can get the repl to evaluate 'str'
  • note the sleep command; I'm not sure how important this is but I thought it prudent (and may have had trouble otherwise) to give the browser some time to reload before trying to send another command to the repl

    • there may be other ways to do this such as not reloading the browser page and simple insert a script tag into the head node of the document using DOM operations
  • now we add in some functionality that will examine the buffer we are writing our tests in and guesses which test function to send to the repl:

        (defun proj1/tests/run (test-module)
          "Pass test invocation string to me/mozrepl/run-after-reload
          ; eg test-module might be 'tests.module1'
          ; command will be: tests.module1(document.getElementById('tests-container'))"
          (me/mozrepl/run-after-reload
           (concat test-module "(document.getElementById('tests-container'))")))
    
        (defun proj1/find-and-run-test-module ()
          "Look for test module signature; if found, send it to proj1/tests/run"
          (interactive)
          (cond
           ((search "tests.module1 = function" (buffer-string))
            (proj1/tests/run "tests.module1"))
           ((search "tests.module2 = function" (buffer-string))
            (proj1/tests/run "tests.module2")) ))
    
  • the first defun takes a test module name generates the particular command I want mozrepl to evaluate within the context of the testing page:

    "tests.module1(document.getElementById('tests-container'))"
    
    • will be evaluated by the repl within the context of my testing page and will run the module1 tests and pass in a reference to a test-container node (on the testing page) that the results will be displayed in
  • the second defun searches the current buffer and looks for a matching piece of text using the elisp (search ...) function ; in this case, the first part of our definition of the test module is used

    • if it is found, then we invoke the first defun with the relevant module
  • finally, we don't want to have to run M-x proj1/find-and-run-test-module when we are in our test file, so we set up a save hook to do it automatically:

        (defun proj1/autotest () (interactive)
          (add-hook 'after-save-hook 'proj1/find-and-run-test-module 'append 'local))  
    
        (defun proj1/autotest-remove () (interactive)
          (remove-hook 'after-save-hook 'proj1/find-and-run-test-module 'local))
    
  • we invoke the first defun [M-x proj1/autotest] in our buffer just once; thereafter whenever we save, our test page will get reloaded and the test module for that page gets executed

  • the second defun allows us to turn this off
  • And that's pretty much it!

Notes

  • Don't forget:
    • you need to keep your browser open on the testing page with the loaded code
    • You will need to start the repl in emacs (M-x run-mozilla)
  • I haven't figured out how to navigate tabs in mozrepl

Concluding Remarks

  • mozrepl allows you to tinker with live webpages via a javascript repl
  • emacs is great at connecting with repls in general
  • this article was just experimenting with combining these two

Related pages

  • The inspiration for this artice was this page on emacswiki
    • this page shows how to program emacs to send commands to mozrepl
    • it highlights comint mode; one of the things that makes emacs an extremely powerful, versatile editor and which we used above