Search code examples
javajavascriptrhino

How can I add methods from a Java class as global functions in Javascript using Rhino?


I have a simple Java class that has some methods:

public class Utils {
    public void deal(String price, int amount) {
        // ....
    }
    public void bid(String price, int amount) {
        // ....
    }
    public void offer(String price, int amount) {
        // ....
    }
}

I would like to create an instance of this class and allow the Javascript code to call the methods directly, like so:

deal("1.3736", 100000);
bid("1.3735", 500000);

The only way I could figure out for now was to use

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.put("utils", new Utils());

and then use utils.deal(...) in the Javascript code. I can also write wrapper functions in Javascript for each method, but there should be a simpler way to do this automatically for all the public methods of a class.


Solution

  • I'm not real familiar with Rhino, but something like this should work:

    for(var fn in utils) {
      if(typeof utils[fn] === 'function') {
        this[fn] = (function() {
          var method = utils[fn];
          return function() {
             return method.apply(utils,arguments);
          };
        })();
      }
    }
    

    Just loop over the properties of utils,and for each one that is a function, create a global function that calls it.

    EDIT: I got this working in a Groovy script, but I had to set utils in the bindings, not on the engine like in your code:

    import javax.script.*
    
    class Utils {
       void foo(String bar) {
          println bar
       }   
    }
    
    ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
    
    engine.eval("""
    for(var fn in utils) {
      if(typeof utils[fn] === 'function') {
        this[fn] = (function() {
          var method = utils[fn];
          return function() {
             return method.apply(utils,arguments);
          };
        })();
      }
    }
    
    foo('foo'); // prints foo, sure enough
    """,new SimpleBindings("utils":new Utils()))