Preface: This is a bad idea with untested code. If you deploy it on a production server bad things will happen.
A few weeks ago I was trolling the Internet and ran across an interesting piece over at John Resig’s blog about Javascript WebWorkers. Basically, WebWorkers are a draft recommendation that allow you to run Javascript functions on a background (non-UI thread) thread. Essentially, they would allow you to do long running computations without hanging the browser’s UI. Pretty neat. Problem is that they are currently only available in Firefox 3.5+, Safari 4, and Chrome 3.0
In my never ending quest to use every buzzword at least once I decided to try and implement a compatibility layer to bring support for WebWorkers to other browsers. The plan was to use Java6’s new embeded Javascript interpreter (it’s just Rhino) to execute the WebWorker code server side and then pipe the output back to the client. Again, this is really just a proof of concept.
There are three parts to the rig: the client Javascript library, a Flex/AS3 application for streaming client to server communication, and a Java application that uses Rhino to execute the Javascript.
Client Javascript
The client Javascript detects the user’s browser and then will define a “Worker” object if the user’s browser doesn’t support WebWorkers. The new “Worker” object uses the Flex application to pass messages back and forth to the server and calls the user’s onmessage function when data arrives from the server.
I sniped the browser detection code from Quirksmode and it seems to work fairly well. The rest of the code is below:
Flex/AS3 Application
The Flex application is basically a dumb conduit between the server and the client. All it really does is pass messages between the Java on the server and the Javascript on the client.
The trickiest part of getting this to work was Adobe’s insane rules for allowing their Socket classes to connect to servers. In order for the client to successfully connect to the server you need to serve a XML policy file from port 843. Additionally, this file can’t be served by a HTTP server but must be a custom server that only spits back the file along with a null carriage return. A detailed description of this abortion is here http://www.adobe.com/devnet/flashplayer/articles/socket_policy_files.html
This really posses two problems. One, you need to be running some random “policy file server” for Flex sockets to be of any use. And two, since 843 is a privileged port, this server can’t be started by a regular user.
The most interesting parts of the ActionScript are probably the snippets that call out to Javascript functions:
ExternalInterface.call("sfWebWorkersSWFReady", true);
Java Server
The most complicated part of this whole thing is probably the Java application that actually executes the WebWorker Javascript. All the communication between the Flex and Java is done entirely with JSON. The server basically does the following:
- Listen for connections from the Flex and accept them when they come in.
- When a message comes in – it can either be a request to create a new web worker or a postMessage() event containing some data for an existing worker.
- If it’s a request for a new worker, the server will download the Javascript file and then execute it inside a Rhino container.
- Otherwise if Flex passed a postMessage() message the server will forward that data to the running web worker.
- The other event that happens is that a web worker can send messages back to the Flex.
Anyway, I tested this on IE7+ and it seemed to work decently well. Per the warning on top I don’t want to leave this running on a live server anywhere.
If you want to get it to actually run, do the following:
- Download the zip of all the sources here.
- Start the JAR in WebWorkerServer/WebWorkerServer.jar with java -jar WebWorkerServer.jar 9999
- On the top of web/sfwwcompat.js change the IP address or the server to where your Java server is located (localhost if you want)
- Open web/wwsha1.html in IE or Chrome 2.0 and you should see stuff happen.
What’s in the box:
- web/ contains the Javascript and a demo.
- WebWorkerConduit/ contains the Flex applicaiton.
- WebWorkerServer/ contains the Java server.
Credits: I borrowed the WebWorker SHA1 implementation from John Resig who adapted it from Ray C Morgan.
So here is another crazy idea. Instead of executing the WebWorker code on the server, would it be possible to dynamically make the WebWorker code re-entrant using setTimeout() on the client where loop structures exist?