I recently had to create my first Java Applet. One of the requirements was that it needed a Javascript API for controlling all the major functions that the applet could perform. Before I started, I wasn’t entirely sure this was even possible. It turns out, the Java plugin used in web browsers has a feature called LiveConnect that enables applet to javascript communication. Java.net has a pretty comprehensive LiveConnect guide here: https://jdk6.dev.java.net/plugin2/liveconnect/, but there was one major issue I had.
My applet used sockets to communicate with a server. In order to use sockets in an applet, you need to sign your jar files. You can find more information about signing jar files here: http://java.sun.com/docs/books/tutorial/deployment/jar/signing.html However, even though I had signed all my jars, I still wasn’t able to open a socket. When I tried, I was getting this exception:
java.security.AccessControlException: access denied (java.net.SocketPermission ....)
Thankfully, one of my co-workers had run into this problem before. The issue is: Java code called directly from Javascript using LiveConnect has no permissions, even if your jars are signed.
For example: in Javascript I call a login() method on my Java object. The login() method needs to open a socket, but since it was called from Javascript, it doesn’t have permission and fails.
My solution was to create a queue that executes inside the code with permissions. I was then able to insert objects into the queue from code without permissions and have them execute with permissions. My queue class:
public class JsEventQueue implements Runnable {
private static final Logger logger = Logger.getLogger(JsEventQueue.class);
private final Thread thread = new Thread(this, "JsEventQueue");
private final BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
private boolean running = true;
public JsEventQueue() {
thread.setDaemon(true);
thread.start();
}
public void run() {
while (running) {
try {
Runnable runnable = queue.take();
if (runnable != null) {
runnable.run();
}
} catch (InterruptedException ie) {
logger.error("Interrupted while waiting for new task.", ie);
}
}
}
public void add(Runnable runnable) {
queue.add(runnable);
}
public boolean isRunning() {
return running;
}
public void setRunning(boolean running) {
this.running = running;
}
}
I instantiate this class when my applet starts and then pass it Runnable objects from outside the permission zone. So, in my example, when I call login() from Javascript, the login method creates a new Runnable object with the login code and passes that to my JsEventQueue reference:
public void login() {
//queue = previously instantiated reference to JsEventQueue object
queue.add(new Runnable() {
public void run() {
/**
* Code for performing login, this code will have permissions.
*/
}
});
}
This is an easy way to call Java code with permissions from Javascript.