Making A Javascript Function Available To Java Code - java

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
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.
Call employeesForStore("downtown") and store its results in a native java String[] or List<String> in a variable called downtownResults.
Same as 2, but call employeesForStore("uptown") and store in variable uptownResults

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.

You can use Rhino API to execute JS code in java
This tutorial covers the examples requested.

Related

XLLoop (Excel 2010) - Integrating a java function that returns an array/list of values

I've started looking at XLLoop as I need a way of getting data from a java system into Excel (2010 version).
I've followed the java SimpleServer example on the front page of the website:
package org.boris.xlloop.util;
import org.boris.xlloop.FunctionServer;
import org.boris.xlloop.handler.*;
import org.boris.xlloop.reflect.*;
public class ServerExample
{
public static void main(String[] args) throws Exception {
// Create function server on the default port
FunctionServer fs = new FunctionServer();
// Create a reflection function handler and add the Math methods
ReflectFunctionHandler rfh = new ReflectFunctionHandler();
rfh.addMethods("Math.", Math.class);
rfh.addMethods("Math.", Maths.class);
rfh.addMethods("CSV.", CSV.class);
rfh.addMethods("Reflect.", Reflect.class);
// Create a function information handler to register our functions
FunctionInformationHandler firh = new FunctionInformationHandler();
firh.add(rfh.getFunctions());
// Set the handlers
CompositeFunctionHandler cfh = new CompositeFunctionHandler();
cfh.add(rfh);
cfh.add(firh);
fs.setFunctionHandler(new DebugFunctionHandler(cfh));
// Run the engine
System.out.println("Listening on port " + fs.getPort() + "...");
fs.run();
}
}
and have successfully been able to integrate simple static java methods into Excel that return one value. E.g.Math.random(), Math.pow(2, 4.45) which both return a single double.
However, I have been unable to integrate any functions that return an array/list of values. For example, in the above we have registered the Maths class (an XLLoop class) for use in Excel. This class contains a static method
public static double[] normalDist(int var0)
The method returns an array of length var0, however when I call this in Excel only one cell is populated with a value, I would have expected var0 cells to have been populated. This seems like a fairly fundamental feature to me, so I'm pretty sure I must be doing something daft.
If there is anyone out there who could help with this it would be greatly appreciated.
I should also mention that I found a similar SO question , linked from a R related SO questions feed. Unfortunately the original SO question has been removed from SO by the poster...
You need to enter the function calling the method that returns an array as a matrix-function in excel: select the cells that shall recieve the returned data, enter the function and hit ctrl-shift-enter.
If the range selected is too small, then only the values fitting in the range are displayed.

Creating a Java Object in Scala

I have a Java class "Listings". I use this in my Java MapReduce job as below:
public void map(Object key, Text value, Context context) throws IOException, InterruptedException {
Listings le = new Listings(value.toString());
...
}
I want to run the same job on Spark. So, I am writing this in Scala now. I imported the Java class:
import src.main.java.lists.Listings
I want to create a Listings object in Scala. I am doing this:
val file_le = sc.textFile("file// Path to file")
Listings lists = new Listings(file_le)
I get an error:
value lists is not a member of object src.main.java.lists.Listings
What is the right way to do this?
Based on what you've said, I think you may be forgetting the differences between Scala syntax and Java syntax.
Try this:
val lists: Listings = new Listings(SomeString)
Please note that specifying the type in Scala is completely optional. Also, use a var if you're going to be changing the value of lists.
The way you have it, Scala is trying to interpret it by its ability to call methods/access values of an object without the '.', so you're actually telling Scala this:
Listings.lists = new Listings(SomeString)

Clojure RT/Compiler: How to Iterate through forms?

I am working on a Java project that has some Clojure involved. I know how to run compile and run clojure code:
public static void main(String[] args) throws Exception {
RT.init();
runCode();
}
public static Object runCode() {
String str = "(ns my-ns)" +
"(defn add [a b] (+ a b))" +
"(println (add 1 2))";
Compiler.load(new StringReader(str));
/* I know how to invoke it: */
Var foo = RT.var("my-ns", "add");
return foo.invoke(1,2);
}
What would be very useful at the point is to have a way to iterate over forms in Java, and in some sense "analyze" the compiler output. Basic things I want to know is:
What is the text source of a form?
What function is being called in a form.
What arguments are being passed to the function (forms are ok)
Be able to do this on top level forms, or drill in as needed.
Is there a way to do this using the clojure compiler, or runtime (or other Java classes in Clojure?) I see such compiler methods as analyze, for example:
Expr target = analyze(C.EXPRESSION, RT.second(form));
Though its not clear to me yet how form was constructed, and there are no Javadoc :-). Do I need to go The Compiler Source and figure out how it works?

Nashorn access non-static Java method

In Java 7 (1.7), I could access a Java method from JavaScript by running this:
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("importClass(net.apocalypselabs.symat.Functions);");
jse.eval("SyMAT_Functions = new net.apocalypselabs.symat.Functions();");
String input = "notify(\"Foo\");"; // This is user input
jse.eval("with(SyMAT_Functions){ "+input+" }");
Which would run the notify() function from the Functions java class:
public class Functions {
private Object someObjectThatCannotBeStatic;
public void notify(Object message) {
JOptionPane.showMessageDialog(null, message.toString());
}
/* Lots more functions in here, several working with the same non-static variable */
}
How do I access the Functions class in Java 1.8 with the Nashorn engine? My goal is to run different code for the first snippet if the user has Java 1.8, while still allowing people with 1.7 to use the app.
I've tried http://www.doublecloud.org/2014/04/java-8-new-features-nashorn-javascript-engine/ , https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/api.html , and How to instantiate a Java class in JavaScript using Nashorn? without luck. None of them seem to allow me the same thing as Java 1.7 did, instead assuming I only want to access static functions and objects.
The most common error I get:
I start with...
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval("var SyMAT_Functions;with (new JavaImporter(Packages.net.apocalypselabs.symat)) {"
+ "SyMAT_Functions = new Functions();}");
...then...
jse.eval("with(SyMAT_Functions){ "+input+" }");
...spits out...
TypeError: Cannot apply "with" to non script object in <eval> at line number 1
I was able to reproduce. First of all, Nashorn doesn't try to make it difficult to use Java objects (non-static or otherwise) in general. I have used it in other projects and not had any major issue converting from Rhino in Java 7 beyond what is covered in the migration guide. However, the issue here appears to deal with the use of the with statement which is "not recommended" and is even disallowed in strict mode of ECMAScript 5.1, both according to MDN.
Meanwhile, I found a thread on the Nashorn-dev mailing list discussing a similar case. The relevant part of the response was:
Nashorn allows only script objects (i.e., objects created by a JS
constructor or JS object literal expression) as scope expression for
"with" statement. Arbitrary objects . . . can not be used as 'scope' expression for
'with'.
In jdk9, support has been added to support script objects mirror other
script engines or other globals (which are instances of ScriptObjectMirror).
It's not the most elegant solution but, without using JDK 9, I was able to get your intended use of with to function by writing a proxy object inside the Javascript to mirror the public API of the Java class:
package com.example;
import javax.script.*;
public class StackOverflow27120811
{
public static void main(String... args) throws Exception {
ScriptEngine jse = new ScriptEngineManager().getEngineByName("JavaScript");
jse.eval(
"var real = new Packages.com.example.StackOverflow27120811(); " +
"var proxy = { doSomething: function(str) { return real.doSomething(str); } }; "
);
jse.eval("with (proxy) { doSomething(\"hello, world\"); } ");
}
public void doSomething(String foo) {
System.out.println(foo);
}
}
Attila Szegedi pointed out the non-standard Nashorn Object.bindProperties function. While it can't be expected to work with anything but the Nashorn engine, it does eliminate the complexity of re-declaring all of the public API inside the proxy object. Using this approach, the first jse.eval(...) call can be replaced by:
jse.eval(
"var real = new Packages.com.example.StackOverflow27120811(); " +
"var proxy = { }; " +
"Object.bindProperties(proxy, real); " // Nashorn-only feature
);
I decided to compile and bundle the "old" Rhino interpreter with my application instead of using Nashorn.
https://wiki.openjdk.java.net/display/Nashorn/Using+Rhino+JSR-223+engine+with+JDK8

call method from existing external .dll. For ex, CopyFileA from kernel32.dll

The task is to call method from an existing dll.
I'm trying to do that on an example of CopyFileA from kernel32.dll.
The method signature is:
Function long CopyFileA(String lpExistingFileName, String lpNewFileName, boolean bFailifExists) Library "kernel32"
This is how I'm trying to do this in java:
public class Test {
static {
System.loadLibrary("D:\\test\\kernel32");
}
public static void main(String[] args) {
(new Test()).CopyFileA("D:\\test\\hi.txt", "D:\\other\\hi.txt", false);
}
public native long CopyFileA(String lpExistingFileName, String lpNewFileName, boolean bFailifExists);
}
I'm getting:
Exception in thread "main" java.lang.UnsatisfiedLinkError: Test.CopyFileA(Ljava/lang/String;Ljava/lang/String;Z)J
All manuals that I've found describes examples when you write C code and then create dll for yourself. So, you implement native method with signature from generated header file.
But here we already have a dll.
Thanks!
The examples you have seen are the best way to go. There is some harness code that needs to be done to enable Java to call into a native method and visa-versa. With out this harness code there is no way for either of them to communicate with each other.
If you are desperate to call CopyFileA then create the harness code in some C/C++ code that then calls CopyFileA.
If you are trying to avoid programming in C/C++ then there is no way for your java to communicate with CopyFileA.
There may be a third party code that may help you. I don't know of any.
This is really simple: everything you need is download jna.jar file and include it into your project.
Bellow I put some code snippet how to solve your task:
Function showWindow = Function.getFunction("kernel32", "CopyFileA");
Object[] params = new Object[3];
params[0] = "D:\\test\\hi.txt";
params[1] = "D:\\other\\hi.txt";
params[2] = false;
Object result = showWindow.invoke(params);

Categories