JSObject is not Thread-Safe
In one of my last projects I was working on Java applet that needed to digitally sign data forms at the client side in a Web browser. I with my team developed the applet and it did the job well. Unfortunately our customer decided to put multiple instances of the
applet in a single Web page. This resulted to unexpected behavior: sometimes (from time to time) the browser window hangs. You can achieve this by refreshing the browser window continuously very fast pressing [F5] key many times). Strange problem, right?
JSObject is not thread-safe
Initially I thought that this is some kind of threading issue because:
- it happens only when we have multiple instances of the applet
- it happens sometimes, not regularly
After experimenting with lots of examples I found that the problem is related to netscape.javascript.JSObject. I created simple applet and it was able to run with multiple instances. After that I added access to the browser DOM by using JSObject.getWindow(this) and the “hang” problem was introduced.
It was clear that the JSObject class is not thread-safe and this causes the unregular hangs of the browser window. When we have several instances of the applet running in different browser windows we don’t have this issue. It appears only when we have several applets on a single Web page. The reason is simple: when multiple applets are running in the same browser window, all of them share the same instance of the JVM (Java Virtual Machine). In the same time all these applets share the same browser window and thus the same browser DOM tree. This means that JSObject.getWindow(this) could be called by multiple threads in the same time.
JSObject is not thread-safe
To overcome this problem we synchronize all code accessing JSObject by JSObject.class. Why JSObject.class? In a single virtual machine this class should be loaded only in the JVM so synchronizing by it will block multiple threads runngin in a single JVM to access JSObject at the same time.
Below is a sample applet that illustrates how to perform such synchronization:
package com.digisign.applet; import javax.swing.JApplet; import javax.swing.JButton; import netscape.javascript.JSObject; @SuppressWarnings("serial") public class TestApplet extends JApplet { private JSObject browserWindow; @Override public void init() { synchronized (JSObject.class) { System.out.println("init"); this.add(new JButton("I am a button")); this.browserWindow = JSObject.getWindow(this); } } @Override public void destroy() { synchronized (JSObject.class) { System.out.println("stop"); if (this.browserWindow != null) { this.browserWindow = null; } System.gc(); } } }
If you remove the synchronization of the code above, and put the applet several times in a single form, it will randomly hang the Web browser.