JSRMI (Javascript Remote Method Invocation) is a portable browser-neutral Javascript library that makes it possible to execute Javascript functions in a browser from a process external to the browser.

JSRMI is not in any way tied to Java's RMI, but provides a similar mechanism. JSRMI is completely decoupled from the core Selenium Javascript API, but it can be used to access the Selenium API from outside the browser.

All of the browser-side JSRMI code resides in the rmi.js script - available in the Selenium distribution.

Browser support

Language support

Libraries for other languages are under way.

How do I use JSRMI from an external process?

Ruby

Just include the jsrmi script in your own:

require "jsrmi"

browser = Selenium::Browser.new.proxy
someArea = browser.document.getElementById("someArea")
someArea.value = "Hello from Ruby #{Time.new}"

This will modify the text of a text area in the browser. Looks strangely familiar to Javascript doesn't it? You can of course call the selenium API too if the browser has loaded the main Selenium page (which also includes the rmi.js script - at least that is the plan - I hope (Aslak)).

How does it work?

(You can safely skip this section if you don't care - this is gory details)

Browser side

The rmi.js script uses the XMLHttpRequest object (available in all compatible browsers) to communicate with the external process. It executes a GET-POST loop which is repeated ad infinitum - pulling JSRMI invocations from the external process with GET and POSTing the results back. This all happens in a separate thread, thereby having minimal impact on the rest of the web page - without causing a page refresh.

The rmi.js script will do a HTTP GET to a URL on the same host/port as the rmi.js script was loaded from. The content returned from the GET (which must comply with the JSRMI protocol) is then translated into Javascript and dynamically executed via Javascript's eval() function.

The result of the function call (typically a Javascript object) is translated back into the JSRMI protocol format and POSTed back to the same URL as the GET.

External process side

The external process typically consists of a library that embeds the following functionality:

A local invocation should translate the invocation to a JSRMI protocol string and put it on the output queue (which jsrmi will GET). It should then wait for the result of the browser side invocation to be put back on the input queue via a POST from jsrmi. Finally it should translate the return value (another JSRMI protocol string) into a native object and return it to the caller.

At any given point in time there should only be one single JSRMI protocol string in one of the queues - depending on the where the invocation is in its lifecycle.

Reference objects

JSRMI allows objects (such as browser side HTMLDocument, HTMLTextField etc) to be transferred back and forth. This is based on a simple mechanism where each object is given a unique id and maintained in a pool on each side. this pool is used to reference and dereference native objects back and forth from the JSRMI protocol strings.

Why would I use JSRMI?

With Selenium

The Selenium browser runtime will load both selenium test scripts and the web pages from the web application you're testing into the browser. Modern browsers don't allow content to be loaded from different hosts (cross browsing security restrictions). A web application being tested will typically be deployed on a server, and therefore the selenium test scripts must be loaded from the same web server. Depending on who's writing the Selenium scripts and executing them, this may or may not be a restriction to its usage.

Under some circumstances it is desirable to keep Selenium test scripts on your local machine (the same as the one running the browser with the Selenium runtime) rather than on a remote web server hosting the web application being tested. Some examples are:

It is important to emphasise that hosting the Selenium scripts on a local machine would also require that the browser loads the web site being tested from the local machine. Aren't we creating new problems by requiring testers to install a full web server environment on their machines? Actually no. The JSRMI libraries in Ruby and Java (and those who may follow) will soon provide a light HTTP proxy server. The local browser will load the remote web site through this HTTP proxy. The browser's security restrictions are satisfied since all content (Selenium runtime, Selenium scripts and the remote website) is loaded through localhost.

Scripting of existing web applications

Think of all boring web form tasks you could automate with JSRMI....

The JSRMI protocol

TODO: describe the format.

How do I implement a JSRMI client library for language X?

Start by understand the inner workings - look at the ruby implementation and study the javascript. (When the JSRMI protocol is better described, studying this code should not be necessary).

It will be to implement a JSRMI client library in a dynamic language than a static language, because dynamic languages allow arbitrary messages (method invocations) to be sent to an object. Most dynamic languages (Ruby, Python, Groovy, Smalltalk) have a generic mechanism to intercept any message and an object message->JSRMI protocol translation logic is trivial to implement based on this.

Guidelines for static languages such as Java and C#

JSRMI clients for static languages such as Java or C# will either have to choose a subset of the Javascript functions and objects that you want to access, or implement some generic invocation mechanism that allows raw JSRMI protocol strings.

The former is more easy to use from a user perspective, but will be restricted in terms of flexibility. The latter is completely generic, but awkward to deal with from a user perspective.

The recommendation is to implement a raw interface to the JSRMI protocol and have a generic dynamic proxy implementation on top of that. This way the API support can easily be extended simply by implementing new interfaces for the Javascript counterparts and generate dynamic proxies on the fly as needed.

Calling functions/methods in an external process from the browser using JSRMI

This is currently not possible.