-
Put jsjbridge.jar in your buildpath and classpath in place of plugin.jar. If using version 1.7 or below, you also need json.jar in your buildpath and classpath.
-
Change imports of
netscape.javascript.*
toau.com.advancedcontrols.jsjbridge.*
. -
Change mentions of class
Applet
toWebpageHelper
. -
Turn your Applet
.jar
or.class
file into a runnable JAR that usesWebpageHelper
as its main class (or havemain
in one of yourWebpageHelper
classes callWebpageHelper.main
). -
Strip or comment any Applet graphics code that is causing compile errors. Support for (slow-changing) graphics may be added at some point.
-
You can no longer read from
System.in
or write toSystem.out
. As a replacement for logging to the Java Console via the standard output, there is aWebpageHelper
instance field callednlog
(native log), which is a java.util.logging.Logger. Logging to this object logs to both the standard error stream (which can be redirected to a file, as described below) and the browser console (unless one logs to levelNativeMessagingLogLevel.STDERR_ONLY
). The level of these loggers can be set via AppletlogLevel
parameters. e.g.
<param name="logLevel" value="FINE" />
The default log level is "INFO".
- The LiveConnect JSObject API should be able to be used unchanged, except that the
eval
method isn't available on the Firefox version (it's considered a security risk), and aJSObject.UNDEFINED
object is now returned when JavaScript returnsundefined
. Also, Java arrays are now passed to JavaScript by value rather than reference. This greatly speeds sending a large amount of data from Java to JavaScript, since there was previously a call to Java for each array access. But it also means that array changes are not automatically propagated back to Java.
-
By default, the JSJBridge browser extension is inactive at all URLs. Go to the JSJBridge extension Preference page in the Extension Manager to set one or more URL prefixes at which the extension becomes active. If you're using Chrome, and your HTML is on the filesystem, remember to enable the "Allow access to file URLs" JavaScript-Java Bridge extension setting.
-
The
applet
/object
HTML tags for your Applets can stay the same. JavaScript objects which represent your Applets must obtained viadocument.getElementById
calls. It should also be OK when the actualgetElementById
calls are being made through a JavaScript library. -
For each Java program (each of which can host more than one
Applet
/WebpageHelper
class) you then need to create a Native Messaging manifest file (Firefox, Chrome).The
name
field in each manifest file (and the basename of the.json
manifest filename) must be the same as the Appletarchive
parameter, with the.jar
suffix removed, and the JSJBridge extension must be given permission to use it. You will need to create and point manifests to a script that runs your WebpageHelper JAR files viajava -jar myHelper.jar
.e.g., for Firefox
{ "name": "<The value of the Applet-element 'archive' parameter without the '.jar' suffix = the basename of original Applet JAR file>", "description": "My Java WebpageHelper", "path": "/path/to/helper_start_script", "type": "stdio", "allowed_extensions": ["[email protected]"] }
It's useful for debugging to have the start-script append standard error output from the program to a log file: e.g.
#!/bin/bash exec java -jar myHelper.jar 2>> /path/to/myJavaProgram.log
Then put or link your manifest files in the correct location for your OS (Firefox, Chrome).
-
You can have as many active Applets as you like in and across tabs, and each program can have more than one
WebpageHelper
class, but only one instance of each Java program runs at any one time, meaning that there can only be one active Applet for each combination of program name andWebpageHelper
class. -
The extension makes Applet DOM objects both ususal
HTMLElement
objects, on which you can get and set normal properties, and also objects on which you can access and set Java fields, and call Java methods.The
initialized
event will fire on theapplet
/object
element when its Javainit()
method has returned, allowing you to delay work until this has occurred.However, because the extension has to check URL prefixes, it is not guaranteed to be active when the
document.DOMContentLoaded
orwindow.load
events fire, or when inline JavaScript is executed.getElementById
calls on Applets will return normalHTMLElement
s rather than Java proxies before thewindow.jsjbridgeActive
event fires, so instantiation of these should wait until then:addEventListener('jsjbridgeActive', function() { var myHelper = document.getElementById('myApplet'); myHelper.addEventListener('initialized', function() { ... }); });
The HTML element can still be accessed before the
window.jsjbridgeActive
event, but it then needs to be reassigned before it can be used to interact with Java:var myHelper = document.getElementById('myApplet'); myHelper.addEventListener('initialized', function() { myHelper = document.getElementById('myApplet'); ... });
-
If an attempt is made to access a nonexisting property of an Applet object, a call to Java is triggered, and the JavaScript expression returns a Promise. The JavaScript execution cycle will continue to execute the code after the Java call before the result is available.
So if you want JavaScript code to do something dependent on a value retrieved from Java, you must either,
(a) Preface your Java-calling expressions with
await
, inside an async function (or in a browser console).e.g. Original code
console.log('Vector size is ' + helper.javaVector.size());
must become either
console.log('Vector size is ' + await helper.javaVector.size());
or
async function showSize(javaVector) { console.log('Vector size is ' + await helper.javaVector.size()); } showSize(javaVector);
The
showSize
call will return immediately, but the logging will be executed when the size is returned from Java.If Java calls a JavaScript method that makes a number of
showSize
calls: e.g.function showSizes(vector1, vector2, vector3) { showSize(vector1); showSize(vector2); showSize(vector3); }
The function as written above will return before making any
size()
calls to Java, causing the calling Java thread to immediately continue. If you instead wish for Java calls to JavaScript functions to block until the function is fully evaluated, as was the case with the Java Plugin, you need to either,(i), If the sequence of
showSize
calls is important, addawait
to eachshowSize
call:async function showSizes(vector1, vector2, vector3) { await showSize(vector1); await showSize(vector2); await showSize(vector3); }
or (ii), allow the calls to execute in any order, but prevent the function from resolving until all have been completed:
async function showSizes(vector1, vector2, vector3) { await Promise.all([ showSize(vector1), showSize(vector2), showSize(vector3) ]); }
or, equivalently, you can explicity return the Promise:
function showSizes(vector1, vector2, vector3) { return Promise.all([ showSize(vector1), showSize(vector2), showSize(vector3) ]); }
or (b) Instead of using
await
, you can put dependent code in athen
block, which can be done at the JavaScript top-level:
helper.javaVector.size().then(size => console.log('Vector size is ' + size));
You can also put await
in front of a Java field access expression. e.g.
if (await helper.initialized) ...
but you can't put await
in front of a Java field assignment. To wait for the assignment you need to wait on its value e.g.
var scanner = await helper.scanner;
scanner.enabled = false;
await scanner.enabled;
- Expression Chains
Java can be called in an expression chain, which will return a Promise that resolves when the value is available for the chain.
However, while JSJBridge knows whether the first property in a call chain on a Java Object is a field or a method, it cannot know the same for later properties in the chain before that result is available (unless JSJBridge pre-cached information on methods and fields in all Java classes that each program has included, which is not currently done).
So instead of writing
var l = await helper.javaVector.get(0).name.length;
one must use a function for all calls in the chain past the first javaVector
one, even for JavaScript properties like the last string length one:
var l = await helper.javaVector.get(0).name().length();
Such functions don't have to be used if the chain is split:
var v0 = await helper.javaVector.get(0);
var name = await v0.name;
var l = name.length;
or, if the Promises are directly handled using then
:
var l = await helper.javaVector.get(0).then(v0 => v0.name).then(name => name.length);
- JavaScript iterable objects except typed arrays become Java
Object
arrays. Typed arrays become Java primitive arrays of the naturalbyte
,short
,int
,long
,float
, ordouble
types. Unsigned typed arrays become the size-equivalent Java signed type, so Java 8+ unsigned methods must be used to get their correct values.