How to store function handles from ScriptManager for later usage? - java

tl;dr:
How do/can I store the function-handles of multiple js-functions in java for using them later? Currently I have two ideas:
Create multipe ScriptEngine instances, each containing one loaded function. Store them in a map by column, multiple entries per column in a list. Looks like a big overhead depending on how 'heavy' a ScriptEngine instance is...
Some Javascript solution to append methods of the same target field to an array. Dont know yet how to access that from the java-side, but also dont like it. Would like to keep the script files as stupid as possible.
var test1 = test1 || [];
test1.push(function(input) { return ""; });
???
Ideas or suggestions?
Tell me more:
I have a project where I have a directory containing script files (javascript, expecting more than hundred files, will grow in future). Those script files are named like: test1;toupper.js, test1;trim.js and test2;capitalize.js. The name before the semicolon is the column/field that the script will be process and the part after the semicolon is a human readable description what the file does (simplified example). So in this example there are two scripts that will be assigned to the "test1" column and one script to the "test2" column. The js-function template basically looks like:
function process(input) { return ""; };
My idea is, to load (and evaluate/compile) all script files at server-startup and then use the loaded functions by column when they are needed. So far, so good.
I can load/evaluate a single function with the following code. Example uses GraalVM, but should be reproducable with other languages too.
final ScriptEngine engine = new ScriptEngineManager().getEngineByName("graal.js");
final Invocable invocable = (Invocable) engine;
engine.eval("function process(arg) { return arg.toUpperCase(); };");
var rr0 = invocable.invokeFunction("process", "abc123xyz"); // rr0 = ABC123XYZ
But when I load/evaluate the next function with the same name, the previous one will be overwritten - logically, since its the same function name.
engine.eval("function process(arg) { return arg + 'test'; };");
var rr1 = invocable.invokeFunction("process", "abc123xyz"); // rr1 = abc123xyztest

This is how I would do it.
The recommended way to use Graal.js is via the polyglot API: https://www.graalvm.org/reference-manual/embed-languages/
Not the same probably would work with the ScriptEngine API, but here's the example using the polyglot API.
Wrap the function definition in ()
return the functions to Java
Not pictured, but you probably build a map from the column name to a list of functions to invoke on it.
Call the functions on the data.
import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;
public class HelloPolyglot {
public static void main(String[] args) {
System.out.println("Hello Java!");
try (Context context = Context.create()) {
Value toUpperCase = context.eval("js", "(function process(arg) { return arg.toUpperCase(); })");
Value concatTest = context.eval("js", "(function process(arg) { return arg + 'test'; })");
String text = "HelloWorld";
text = toUpperCase.execute(text).asString();
text = concatTest.execute(text).asString();
System.out.println(text);
}
}
}
Now, Value.execute() returns a Value, which I for simplicity coerce to a Java String with asString(), but you don't have to do that and you can operate on Value (here's the API for Value: https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Value.html).

Related

Nashorn put compiledsript into engine scope

I have two js files,
one is js library
second one is a simple script usually somewhat around 50 lines, that needs to access functions from the library.
In my project im trying to precompile all javascripts during my application startup, and then at runtime only invoke CompiledScripts with desired parameters.
I ended up with the following code
static String LIBRARY = "function hello(arg) {return 'Hello ' + arg;};";
static String SCRIPT = "hello(arg)";
public static void main(String... args) throws Exception {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("Nashorn");
Compilable compilable = ((Compilable) engine);
CompiledScript compiledLib = compilable.compile(LIBRARY);
compiledLib.eval();
CompiledScript actualScript = compilable.compile(SCRIPT);
Bindings helloParams = new SimpleBindings();
helloParams.put("arg","world");
ScriptObjectMirror result = (ScriptObjectMirror) actualScript.eval(helloParams);
System.out.println(result);
}
But this code throws an error
> compiledScript.eval(helloParams);
<eval>:1 ReferenceError: "hello" is not defined
How can i access context of "compiledLib" (ie methods and variables) from within the "actualScript"?
Compiling doesn't register the hello() function, it just parses the JavaScript code.
You need to execute the code for the function to be registered.
Remember, in JavaScript, there is very little difference between these two statements, except that function declarations are hoisted and can therefore be used before the declaration statement:
function hello(arg) {return 'Hello ' + arg;};
var hello = function(arg) {return 'Hello ' + arg;};
There is therefore little reason to separately compile the LIBRARY code, you just run it and save off all the created global variables, which are the library methods. E.g. after executing your LIBRARY code, you'll have a global variable named hello.
ScriptEngine engine = new ScriptEngineManager().getEngineByName("Nashorn");
Compilable compilable = ((Compilable) engine);
// Process LIBRARY code
Bindings bindings = new SimpleBindings();
engine.eval(LIBRARY, bindings);
// Compile SCRIPT code
CompiledScript actualScript = compilable.compile(SCRIPT);
// Run SCRIPT code
bindings.put("foo", "world");
Object result = actualScript.eval(bindings);
System.out.println(result);
Output
Hello world

Updating pre-existing documents in mongoDB java driver when you've changed document structure

I've got a database of playerdata that has some pre-existing fields from previous versions of the program. Example out-dated document:
{
"playername": "foo"
}
but a player document generated under the new version would look like this:
{
"playername": "bar",
"playercurrency": 20
}
the issue is that if I try to query playercurrency on foo I get a NullPointerException because playercurrency doesn't exist for foo. I want to add the playercurrency field to foo without disturbing any other data that could be stored in foo. I've tried some code using $exists Example:
players.updateOne(new Document("playername", "foo"), new Document("$exists", new Document("playername", "")));
players.updateOne(new Document("playername", "foo"), new Document("$exists", new Document("playercurrency", 20)));
My thought is that it updates only playercurrency because it doesn't exist and it would leave playername alone becuase it exists. I might be using exists horribly wrong, and if so please do let me know because this is one of my first MongoDB projects and I would like to learn as much as I possibly can.
Do you have to do this with java? Whenever I add a new field that I want to be required I just use the command line to migrate all existing documents. This will loop through all players that don't have a playercurrency and set it to 0 (change to whatever default you want):
db.players.find({playercurrency:null}).forEach(function(player) {
player.playercurrency = 0; // or whatever default value
db.players.save(player);
});
This will result in you having the following documents:
{
"playername" : "foo",
"playercurrency" : 0
}
{
"playername" : "bar",
"playercurrency" : 20
}
So I know that it is normally frowned upon on answering your own question, but nobody really posted what I ended up doing I would like to take this time to thank #Mark Watson for answering and ultimately guiding me to finding my answer.
Since checking if a certain field is null doesn't work in the MongoDB Java Driver I needed to find a different way to know when something is primed for an update. So after a little bit of research I stumbled upon this question which helped me come up with this code:
private static void updateValue(final String name, final Object defaultValue, final UUID key) {
if (!exists(name, key)) {
FindIterable iterable = players.find(new Document("_id", key));
iterable.forEach(new Block<Document>() {
#Override
public void apply(Document document) {
players.updateOne(new Document("_id", key), new Document("$set", new Document(name, defaultValue)));
}
});
}
}
private static boolean exists(String name, UUID key) {
Document query = new Document(name, new Document("$exists", true)).append("_id", key);
return players.count(query) == 1;
}
Obviously this is a little specialized to what I wanted to do, but with little revisions it can be easliy changed to work with anything you might need. Make sure to replace players with your Collection object.

Calling Groovy scripts from Java and refreshing the Groovy scripts periodically

I want to call the Groovy scripts from Java and refresh the Groovy scripts periodically.
For example ,
public class AppTest {
public static void main(String args[]) throws Exception {
TestVO test = new TestVO();
AnotherInput input = new AnotherInput();
test.setName("Maruthi");
input.setCity("Newark");
GroovyClassLoader loader = new GroovyClassLoader(AppTest.class.getClassLoader());
Class groovyClass = loader.parseClass(new File("src/main/resources/groovy/MyTestGroovy.groovy"));
GroovyObject groovyObject = (GroovyObject) groovyClass.newInstance();
Object[] inputs = {test,null};
Map<String,String> result = (Map<String, String>)groovyObject.invokeMethod("checkInput", inputs);
System.out.println(result);
}
}
And my Groovy script is
class MyTestGroovy {
def x = "Maruthi";
def checkInput = { TestVO input,AnotherInput city ->
if(input.getName().equals(x)) {
input.setName("Deepan");
println "Name changed Please check the name";
} else {
println "Still Maruthi Rocks";
}
Map<String, String> result = new HashMap<String,String>();
result.put("Status", "Success");
if(city != null && city.getCity().equalsIgnoreCase("Newark")) {
result.put("requested_State", "Newark");
}
return result;
}
def executeTest = {
println("Test Executed");
}
}
How efficient my memory would be managed when I create multiple instances of groovy script and execute the script. Is it advisable to use a number of Groovy scripts as my customized rule engine. Please advise.
It is usually better to have several instances of the same script, than parsing the class every time you want to create an instance. Performance wise that is because compiling the script takes some time, you have to pay in addition to creating an instance. Memory wise you use up the number of available classes up faster. Even if old classes are collected, if you have many scripts active, it can happen... though that normally means hundreds or even thousands of them (depends on the jvm version and your memory settings)
Of course, once the script changed, you will have to recompile the class anyway. So if in your scenario you will have only one instance of the class active at the same time, and a new instance is only required after a change to the source, you can recompile every time.
I mention that especially, because you might even be able to write the script in a way, that let's you reuse the same instance. But it is of course beyond the scope of this question.

How to use external javascript functions in java class

I have javascript file (jalali.js) which it have a lot of functions.
I want to call one of this functions in my java web application project (I mean somefile.Class file)
I had some research and i found these two methods:
ScriptEngineManager factory = new ScriptEngineManager();
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.eval("print('Hello, World')");
But I can not understand how to call my js file (jalali.js) and how should i call my function
I will put function detail code here (from jalali.js)
JalaliDate.gregorianToJalali = function(g_y, g_m, g_d)
{
g_y = parseInt(g_y);
g_m = parseInt(g_m);
g_d = parseInt(g_d);
var gy = g_y-1600;
var gm = g_m-1;
var gd = g_d-1;
...
...
return [jy, jm, jd];
}
I want to use that function in my java application (MyClass.class)
public class TaskListModel extends BaseModel{
private Date gDate;
private String jalaliDate;
public void setGDate(Date gDate) {
this.gDate= gDate;
this.jalaliDate = Here i need call the js function ;
}
Well if this is the Rhino engine from 1.6 Java, then you can evaluate the code in jalali.js line by line - keeping the instance of engine alive through the runtime of the file. Also you can then execute the function like this: engine.eval("myfunction(arg1, arg2);");
I am not sure what you are trying to achieve
Step1. Read a line of the jalali.js file
Step2. engine.eval() the line
Step3. check if EOF - if yes, go to Step4 else go to Step1
Step4. engine.eval("your_function(arg1, arg2);");
a simple solution would be you read the whole file and append to it the function call you want to make and pass the modified contents to the eval method. so the final content passed to eval would take the form of
//
script in your .js file
//
return functionInYourJs(arg1, arg2);
Probably once the file is read you can cache it to avoid repeated disk reads.

How do I call this object to return all strings it finds?

I have the following code that defines a getParts method to find a given Part Name and Part Number in the system. Note that this code comes from our system's API, so if no one can help I'll just delete this question. I figured someone could potentially see a solution or help me along the way.
<%! private QueryResult getParts( String name, String number )
throws WTException, WTPropertyVetoException {
Class cname = wt.part.WTPart.class;
QuerySpec qs = new QuerySpec(cname);
QueryResult qr = null;
qs.appendWhere
(new SearchCondition(cname,
"master>name",
SearchCondition.EQUAL,
name,
false));
qs.appendAnd();
qs.appendWhere
(new SearchCondition(cname,
"master>number",
SearchCondition.EQUAL,
number,
false));
qr = PersistenceHelper.manager.find(qs);
System.out.println("...found: " + qr.size());
return qr;
}
%>
But I would like to allow the user more flexibility in finding these parts. So I set up conditional statements to check for a radio button. This allows them to search by part name and part number, find all, or search using a wildcard. However, I'm having trouble implementing the two latter options.
To attempt to accomplish the above, I have written the below code:
<%
String partName = request.getParameter("nameInput");
String partNumber = request.getParameter("numberInput");
String searchMethod = request.getParameter("selection");
//out.print(searchMethod);
QueryResult myResult = new QueryResult();
if(searchMethod.equals("search"))
myResult = getParts(partName, partNumber);
else if(searchMethod.equals("all"))
{
//Should I write a new function and do this?
//myResult = getAllParts();
//or is there a way I could use a for each loop to accomplish this?
}
//else if(searchMethod.equals("wildcard"))
//get parts matching %wildcard%
while(myResult.hasMoreElements())
{
out.print(myResult.nextElement().toString());
}
%>
Basically, it accepts user input and checks what type of search they would like to perform. Is there an easy way to pass all the values into the myResult object? And likewise for the wildcard search? Like I said before, it may be futile trying to help without access to the API, but hopefully it isn't.
Thanks!
You can (and should) reuse the function, but in order to do so, you will need a part name and number (as those are its input parameters). So for the multi-result options you will need to get a list/collection of part names+numbers and feed them individually to the function, then collect the result in the format that is most appropriate for your needs

Categories

Resources