A somewhat contrived example to illustrate my question. Imagine we have some library of javascript functions that is already maintained and updated daily by an army of frontend devs. To be specific, imagine one such function looks like this:
function employeesForStore(store) {
var dictionary = {
"downtown": ["Joe", "Mary", "Steve"],
"uptown": ["Jules", "Vincent", "Matt"],
// and so on for hundreds of locations
};
return dictionary[store];
}
NOTE: Please ignore the details of this function's implementation. The actual function may be far more complex than simple JSON dictionary lookups, and assume we don't know any implementation details about the js function. All we know is it takes a String argument and returns and array of Strings.
Now we would like to take advantage of this function in our Java code. That is, in our Java code, we'd like to "load" this function, and then be able to call it multiple times, passing it String
args and receiving String[]
or ArrayList<String>
results.
From searching SO and google so far, I understand that this will involve using:
javax.script.ScriptEngineManager
javax.script.ScriptEngine
scriptEngine.getContext()
for passing values into the function and receiving results. I am a bit hazy on the details of the above, especially since most examples I've found involve running javascript code a single time, rather than making javascript function available to Java.
employeesForStore("downtown")
and store its results in a native java String[]
or List<String>
in a variable called downtownResults
.employeesForStore("uptown")
and store in variable uptownResults
Calling a JavaScript source in Java is pretty simple. If we develop a Java application in which we must use JavaScript, we will create the script file separately, then include and call it in the Java source to run the desired function.
We'll connect JavaScript to Java by creating a new instance of a Java class using the JavaScript new keyword. Example 1 shows a function named browse() that opens a native "file dialog" using the built-in java. awt. Frame and java.
getEngineByName("JavaScript"); // JavaScript code in a String String script1 = "function hello(name) {print ('Hello, ' + name);}"; String script2 = "function getValue(a,b) { if (a===\"Number\") return 1; else return b;}"; // evaluate script engine.
JavaScript also includes functions to control Java applets, and Java applets can access JavaScript functions. By integrating these two Web languages, you can have the best of both worlds, allowing for many complicated applications.
Create an interface to act as a facade to your JavaScript code.
Here is an example using the Rhino implementation embedded in Oracle's Java 1.7 implementation:
package demo;
import java.io.*; import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import javax.script.*;
public class StoreData {
public static interface Stores {
public String[] employees(String store);
}
public static Stores stores() throws IOException, ScriptException {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("JavaScript");
AtomicReference<Stores> ref = new AtomicReference<>();
engine.put("ref", ref);
String adapt = "ref.set("
+ "new Packages.demo.StoreData.Stores({employees:employeesForStore})"
+ ");";
try (Reader myFns = new FileReader("my_functions.js")) { // TODO encoding
engine.eval(myFns);
engine.eval(adapt);
return ref.get();
}
}
public static void main(String[] args) throws IOException, ScriptException {
List<String> employees = Arrays.asList(stores().employees("uptown"));
System.out.println(employees);
}
}
By specifying an interface we let Rhino coerce the JavaScript types to Java types (String, String[], etc.)
The JRE spec makes no guarantees about what scripting engines should be provided so it may be wise to rely on an external engine. I don't know if Nashorn will change this.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With