简体   繁体   中英

Making A Javascript Function Available To Java Code

Problem Description

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
  • and perhaps 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.

Example Code I'd Like To See

  1. Assuming the js function is in the file "my_functions.js", load that file into Java so all of its functions will be available for use.
  2. Call employeesForStore("downtown") and store its results in a native java String[] or List<String> in a variable called downtownResults .
  3. Same as 2, but call employeesForStore("uptown") and store in variable uptownResults

You can use Rhino API to execute JS code in java

This tutorial covers the examples requested.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM