R
Richard Maher
Hi,
This is directly related to "fix" for SDN Bug ID 6742814: -
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6742814
A couple of relevant-sounding quotes from that bug report : -
{
The rules for initiating JavaScript-to-Java and Java-to-JavaScript
calls (which will be formalized in the forthcoming new LiveConnect
specification) are:
- JavaScript-to-Java calls against a given applet block until that
applet has completed init(), or
- that applet initiates a Java-to-JavaScript call in init().
}
The definition of "initiates a Java-to-JavaScript" in that last line appears
to have been further broadened and amplified to include: -
{
- If a request comes to the browser from an applet to fetch the
JavaScript window object corresponding to the applet, drain the
queued up messages corresponding to JavaScript-to-Java calls,
which would otherwise occur when init() was completed.
}
This was raised as an issue for me in comp.lang.java on the 22-April-2009
and Mark Space kindly pointed out that the addition of "synchronized" to the
relevant Applet methods would achieve the desired results, with the worker
methods only being invoked after the Applet had completed its initialization
in the init() method. So everything was peachy until I found that Google
Chrome can, and often does, choose the wrong thread to execute the JS to
Java upcall when dealing with more than one instance of the Applet in the
browser instance. (Different tabs in my case)
You may recall that my Javascript to invoke the Applet looked like this: -
document.body.appendChild(appletDiv);
tier3Chan = document.getElementById(appletId);
var userAuthorized = tier3Chan.isAuthorized();
}
catch(err) {
alert("Err =" + err.description);
tier3Chan = null;
};
What "normally" happens (Firefox, IE, Chrome-[with-Applet-Instance/Tab-1])
is that: -
1) The "getElementById(appletId)" loads the Applet and calls init() in a
thread the JVM calls: -
"thread applet-tier3Client/Tier3Application-1"
My package is "tier3Client" and the class is "Tier3Application" and the
applet instance is "1".
2) The tier3Chan.isAuthorized() method (JS->JAVA upcall) is executed on a
new LiveConnect thread called: -
"Applet 1 LiveConnect Worker Thread"
Again the "1" relating to the applet instance in the browser. When the
applet is loaded again via pages in other tabs the instance number is
incremented and new threads created.
3) I also instantiate another Thread that can call down to JS but I don't
believe that to be relevant at this stage.
4) When my init() method calls JSObject.getWindow(this) Chrome seeks to
"drain the queued up messages corresponding to JavaScript-to-Java calls,
which would otherwise occur when init() was completed" and my isAuthorized()
method is invoked on the "Applet 1 LiveConnect Worker Thread".
5) Because both my overridden init() method and my isAuthorized() methods
are synchronized on the Applet instance's object, the isAuthorized() method
is forced to wait until initialization is complete and authorization has
been checked.
A tad complicated but it worked and I was happy with it.
What appears to go wrong with Chrome on the 2nd -(usually the 2nd but then
it might work/fail for additional tabs - the first page always works)- tab
with an applet invoking page is that the isAuthorized() upcall gets executed
on the "thread applet-tier3Client/Tier3Application-2" Thread. The same
Thread that init() was/is executing on!!!
Now my understanding of the Java threading model is that, to support
recursion, a Thread cannot lock/mutex/synchronize itself out of an object.
So my "synchronized" init() and isAuthorized() methods on the Applet 2
instance don't amount to a hill o' beans and my authorization check occurs
*before* I've determined if the user is authorized and I've had a chance to
set the variable :-(
To summarize, Chrome has a bug where it can permit an upcall to execute on
the "thread applet-tier3Client/Tier3Application-(n)" thread instead of the
"Applet (n) LiveConnect Worker Thread". Try as I might I cannot create a
"simple" reproducer for this :-( My Applet is quite complex and I'm
clutching at straws to guess what algorithm Chrome uses when looking for
threads to allocate work to, or for Threads to publish their availability.
Can someone please shed any light on this? Is there a work around? Is there
a useful Chrome bug-logging forum that will even look at the source without
a reproducer to go on?
Look, I know I shot my mouth off before about this being a race condition
where the isAuthorized() was being called *before* the init() but that was
because I couldn't cope with the concept of synchronized Applet instance
methods executing at the same time.
Please help if you can.
Cheers Richard Maher
This is directly related to "fix" for SDN Bug ID 6742814: -
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6742814
A couple of relevant-sounding quotes from that bug report : -
{
The rules for initiating JavaScript-to-Java and Java-to-JavaScript
calls (which will be formalized in the forthcoming new LiveConnect
specification) are:
- JavaScript-to-Java calls against a given applet block until that
applet has completed init(), or
- that applet initiates a Java-to-JavaScript call in init().
}
The definition of "initiates a Java-to-JavaScript" in that last line appears
to have been further broadened and amplified to include: -
{
- If a request comes to the browser from an applet to fetch the
JavaScript window object corresponding to the applet, drain the
queued up messages corresponding to JavaScript-to-Java calls,
which would otherwise occur when init() was completed.
}
This was raised as an issue for me in comp.lang.java on the 22-April-2009
and Mark Space kindly pointed out that the addition of "synchronized" to the
relevant Applet methods would achieve the desired results, with the worker
methods only being invoked after the Applet had completed its initialization
in the init() method. So everything was peachy until I found that Google
Chrome can, and often does, choose the wrong thread to execute the JS to
Java upcall when dealing with more than one instance of the Applet in the
browser instance. (Different tabs in my case)
You may recall that my Javascript to invoke the Applet looked like this: -
document.body.appendChild(appletDiv);
tier3Chan = document.getElementById(appletId);
var userAuthorized = tier3Chan.isAuthorized();
}
catch(err) {
alert("Err =" + err.description);
tier3Chan = null;
};
What "normally" happens (Firefox, IE, Chrome-[with-Applet-Instance/Tab-1])
is that: -
1) The "getElementById(appletId)" loads the Applet and calls init() in a
thread the JVM calls: -
"thread applet-tier3Client/Tier3Application-1"
My package is "tier3Client" and the class is "Tier3Application" and the
applet instance is "1".
2) The tier3Chan.isAuthorized() method (JS->JAVA upcall) is executed on a
new LiveConnect thread called: -
"Applet 1 LiveConnect Worker Thread"
Again the "1" relating to the applet instance in the browser. When the
applet is loaded again via pages in other tabs the instance number is
incremented and new threads created.
3) I also instantiate another Thread that can call down to JS but I don't
believe that to be relevant at this stage.
4) When my init() method calls JSObject.getWindow(this) Chrome seeks to
"drain the queued up messages corresponding to JavaScript-to-Java calls,
which would otherwise occur when init() was completed" and my isAuthorized()
method is invoked on the "Applet 1 LiveConnect Worker Thread".
5) Because both my overridden init() method and my isAuthorized() methods
are synchronized on the Applet instance's object, the isAuthorized() method
is forced to wait until initialization is complete and authorization has
been checked.
A tad complicated but it worked and I was happy with it.
What appears to go wrong with Chrome on the 2nd -(usually the 2nd but then
it might work/fail for additional tabs - the first page always works)- tab
with an applet invoking page is that the isAuthorized() upcall gets executed
on the "thread applet-tier3Client/Tier3Application-2" Thread. The same
Thread that init() was/is executing on!!!
Now my understanding of the Java threading model is that, to support
recursion, a Thread cannot lock/mutex/synchronize itself out of an object.
So my "synchronized" init() and isAuthorized() methods on the Applet 2
instance don't amount to a hill o' beans and my authorization check occurs
*before* I've determined if the user is authorized and I've had a chance to
set the variable :-(
To summarize, Chrome has a bug where it can permit an upcall to execute on
the "thread applet-tier3Client/Tier3Application-(n)" thread instead of the
"Applet (n) LiveConnect Worker Thread". Try as I might I cannot create a
"simple" reproducer for this :-( My Applet is quite complex and I'm
clutching at straws to guess what algorithm Chrome uses when looking for
threads to allocate work to, or for Threads to publish their availability.
Can someone please shed any light on this? Is there a work around? Is there
a useful Chrome bug-logging forum that will even look at the source without
a reproducer to go on?
Look, I know I shot my mouth off before about this being a race condition
where the isAuthorized() was being called *before* the init() but that was
because I couldn't cope with the concept of synchronized Applet instance
methods executing at the same time.
Please help if you can.
Cheers Richard Maher