Questions about Nashorn - JSObject - java

I want to create a JSObject and fill it with properties (kind of like a HashMap), but without casting the result of an eval("({})"), because I would think that constantly evaluating such a thing would really have an impact on performance. Is there a way?

If you want to use script objects like Maps, you might as well use java.util.HashMap instances from Nashorn scripts! In addition to supporting the usual java method calls, Nashorn's linker special cases java.util.Map instances and supports keys-as-property names idiom. This is more efficient than using a Javascript object as a Map.
$ jjs
jjs> var m = new java.util.HashMap
jjs> m.foo = "bar"
bar
jjs> m.foo
bar
jjs> m.get("foo")
bar
jjs> m.put("js", "nashorn")
null
jjs> m.js
nashorn
But, if you insist on using JS-object-as-map, then you can do the eval and cast to JSObject you mentioned. You may want to measure the perf. hit (which is assumed!) before making any further changes!
You can also get hold of JS "Object" constructor object and 'cache' it for repeated object creation from Java code. You can then call newObject() method on it to create a new empty object.
import javax.script.*;
import jdk.nashorn.api.scripting.JSObject;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// get JS "Object" constructor object
JSObject objConstructor = (JSObject)e.eval("Object");
// call 'newObject' on Object constructor
JSObject jsObj = (JSObject) objConstructor.newObject();
// fill properties of the new empty script object
jsObj.setMember("foo", "bar");
// expose the new JS object as global var!
e.put("obj", jsObj);
// print the object as a JSON string
e.eval("print(JSON.stringify(obj))");
}
}
Note that the above scheme works for any user-defined constructor function as well. For eg. if you want to create objects using specific user defined constructor function, you just have to replace
JSObject objConstructor = (JSObject)e.eval("Object");
with
JSObject objConstructor = (JSObject)e.eval("MyConstructorFunc");
(assuming you've eval'ed code to define MyConstructorFunc function earlier).
The rest of the code is same as above.

Related

JavaFX WebView: how to check if form is valid?

Is there a way to tell if a form is valid from Java?
Basically, a method that returns true if all input fields constrains are satisfied and false otherwise.
Strangely enough, org.w3c.dom.html.HTMLFormElement doesn't actually have a checkValidity() method.
EDIT:
Even more strangely, the implementation com.sun.webkit.dom.HTMLFormElementImpl does support the method checkValidity(). Unfortunately, the package com.sun.webkit is not accessible directly and thus the method is unavailable.
DOM objects like HTMLFormElement only model structure. They are not capable of executing JavaScript.
However, WebEngine itself does have a JavaScript interpreter, which you can invoke using the executeScript method:
boolean valid = (Boolean) webView.getEngine().executeScript(
"document.forms[0].checkValidity();");
The checkValidity() method is documented here and here.
Cast the form to JSObject
Most HTML Java elements, including HTMLFormElement, can be directly cast to the JavaScript object JSObject. It is then trivial to validate the form.
Example:
JSObject jsObject = (JSObject) form;
boolean valid = (boolean) jsObject.call("checkValidity");

How to send and recive serialized object in jxbrowser from java

I have to send a serialized object from java to javascript with jxbrowser and I do this like this
String json = objectMapper.writeValueAsString(value);
JSValue window = browser.executeJavaScriptAndReturnValue("window");
window.asObject().setProperty(requestName, json);
As far as I know it will set a object in global window as requestName? It is true?
And in another way how I can read this object from java site. This code is ok?
JSValue window = browser.executeJavaScriptAndReturnValue("window."+requestName);
T t = objectMapper.readValue(window.toString(), clazz))
Thanks in advance
Hi there are two aspects here.
Javascript execution context
JSValue that can be a plain value or a JavaScript object.
Once you invoke executeJavaScriptAndReturnValue your execution context is complete. And you can evaluate the returned object. This returned object can be a Java script object with functions in which case you can access it.
Lets say that your JavaScriptObject has a method helloWorld which accepts string.
JSValue document = browser.executeJavaScriptAndReturnValue("myJavascriptObject");
JSValue write = document.asObject().getProperty("helloWorldMethod");
write.asFunction().invoke(document.asObject(), "To Me");
This way we have passed the "To Me" string to the helloWorldMethod.
You can also set properties on the Object and invoke later another method. If this method uses this property, than within the next execution it will be taken into account:
JSValue document = browser.executeJavaScriptAndReturnValue("myJavascriptObject");
JSValue write = document.asObject().getProperty("helloWorldMethod");
document.asObject().setProperty("shouldISayGoodByeInstead",true)
write.asFunction().invoke(document.asObject(), "To Me");
The property shouldISayGoodByeInstead will be evaluated as part of the second execution which happens when helloWorldMethod is invoked, not during the first execution of executeJavaScriptAndReturnValue.
If I understand you correctly, you want to set a JavaScript object through JSON. This can be achieved via the JSONString. You can check the example here: https://jxbrowser.support.teamdev.com/support/solutions/articles/9000013064-working-with-json
After that, you want to get the JSON object. For this case, there is a method
JSObject.toJSONString()
So if we change your sample in the following way, it should work as expected:
String json = objectMapper.writeValueAsString(value);
JSValue window = browser.executeJavaScriptAndReturnValue("window");
window.asObject().setProperty(requestName, new JSONString(json));
JSValue window = browser.executeJavaScriptAndReturnValue("window."+requestName);
T t = objectMapper.readValue(window.asObject().toJSONString(), clazz))

is it possible to have a mixed variable in Java like in PHP?

For example in PHP:
<?php
$my_var = 1;
$my_var = [ "1" ]; // no problem
In Java, I tried to make a Class with a mixed argument:
class MyClass {
public String my_var;
}
MyClass c = new MyClass();
c.my_var = "ok";
c.my_var = 1; // error during compilation time
The reason I am asking that is because I am using jersey with json and I try to make a Feedback Class.
One argument in my Feedback class is the data to send back to the front-end in an Ajax manner. Sometimes data is a normal string:
`{ "data" : "hello world" }`
Sometimes it can be an array
`{ "data" : [ "hello", "world" ] }` or `{ "data" : [ { "id": 1 }, { "id": 2 }, ... ] }`
I am a real noobies when it comes to the Java language and libraries. The only solution I can think of now is to override the toString method for the objects involved in the Json encoding but "toString"'ing a List will give me something like that,
`{ "data" : "[ ...]" }` which is incorrect.
you can do this:
Object c = new Object();
c = new String("hi"); //valid
c = new Integer(1); //valid
And then to check what type of variable it is you can use instanceof
You can also make it an array or a list, and if you want to use toString on it generically to print out any object, you can make your own list class with an overridden toString method that prints it nicely, and use that class in your object
using objects with nice toString methods means that you won't have to use instanceof if all you want to do is get a string representation of the object
Java is a statically typed language; and "dynamics" are limited to things such as assigning sub class values to a super class variable, like:
Object o = "a string";
o = new Double(42.0);
So it looks like o can be both, String and Integer. But as said; that is because Object is a super type of both those classes.
This for example:
String s = "meow";
s = 42;
leads to a compiler error. What would work again would be s = Integer.toString(42) for example.
Similarly, you could do:
Object o = new int[5];
because in Java any reference type (including arrays) is a sub class of Object.
And just to be precise: the Java people think this is an advantage. Because it allows more checking at compile time - catching many errors that require you to write/run unit tests in those dynamic languages where a duck can meow like a cat and still be barking dog in the end.
Coming back to your question: from a Java point of view, an array is not the same as a string. And therefore you would not a low such a JSON representation. In other words: I would first try to change the JSON format.
If that is not possible, I would look into customized JSON de/serialization code - in order to have the framework decide which case is given and do the appropriate thing. Just as inspiration, you can have a look how this can be done with gson.
Currently you can't do that in Java yet, so you'd have to make an indirect workaround, but there are plans to implement typeless objects/variables in Java version 10 (currently at 8, almost moved to 9)
Think oops way,
Have an Interface like FeedBackResponse , Have different implementation classes for each of the cases, make them implement FeedBackResponse.
Each class will have different attributes.
Serialize the object using json library.
Ex,
FeedBackResponse response = getResponse("Some Service");
return jsonLib.serialize(response);
Factory
private FeedBackResponse getResponse(String attr){
// based on attr create ur object here.
}

Matching Javascript RegEx using Nashorn

How would I evaluate input with a ECMA 262 compliant regular expression?
After some reading I found out the Java 8's javascript engine nashorn can help me to do that. How can I use nashorn script engine to match a regular expression?
Assume you have a file regex.js containing a method used to determine if a given String matches a given regular expression:
var check = function(regex, str) {
return new RegExp(regex).test(str);
};
You can evaluate this function using the Nashorn script engine, and call it with the following code:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("regex.js"));
Invocable invocable = (Invocable) engine;
Object result = invocable.invokeFunction("check", "\\d+", "1");
System.out.println(result);
In order to call a function, you first have to cast the script engine to Invocable. This interface is implemented by the NashornScriptEngine and defines a method invokeFunction to call a JavaScript function for a given name. The rest of the method arguments are then directly passed as arguments to the JavaScript method.
Note that it is also possible to do this without an external JavaScript file. It is also possible to avoid the cast to Invocable by asking the script engine to evalute a String. This is the same code with those two ideas:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval("function check(regex, str) { return new RegExp(regex).test(str); }");
Object result = engine.eval("check('\\\\d+', 1);");
System.out.println(result);
When passing variables from Java to JavaScript, you have to be very careful about the proper escaping of characters: the rule are not the same for the two languages.
Perhaps something like this:
ScriptEngineManager manager = new ScriptEngineManager();
ScriptEngine se = manager.getEngineByName("nashorn");
se.put("str", "the quick brown fox");
se.put("rgx", "/quick/");
Object result = se.eval("str.matches(rgx).length");
System.out.println(result);
You need to run with Java8 to get the "nashorn" engine. The argument to se.eval(String) is the javascript. I kept it simple for this example. You can do plenty other stuff in terms of regex. See here.
Using Java 8 you can use the Nashorn JavaScript engine via the package javax.script using the nashorn identifier.
Here's an example:
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine nashorn = engineManager.getEngineByName("nashorn");
String ecmaScript = //YOUR SCRIPT HERE
//add any additional variables here
Object returnVal = nashorn.eval(ecmaScript);
SOURCE: http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html
You'll want to get familar with the ScriptEngine class and you'll need to do quite a bit of testing and trial & error to handle the data that's returned since the returned data won't be type safe. The debugger will be your best friend during this process.

Passing json data from javascript to Java

I'm trying to pass json data from javascript to java, like below. The idea is to pass the data to the java code as a javascript object (and not a a string which I know can be done ). I've tried the code below without success - idea was to use NativeJson.stringify to convert from a javascript object to a Java string, however this results in an Undefined instance instead of the expected string. Any idea on this could be achieved much appreciated.
in javascript file "test.js"
parser.fct ( {"abc":123,"def":456} )
in java
//1.binds javascript calls to Java
...
ScriptEngine engine = new ScriptEngine...
Bindings bindings = engine.createBindings();
bindings.put("parser", new Parser());
engine.eval("test.js");
//2. in Parser class
public void fct(Object obj){
Context ctx = Context.enter();
ScriptableObject scope = ctx.initStandardObjects();
Object json = NativeJSON.stringify(ctx, scope, obj, null,null);
//json returned is of type Undefined
}
Could not find a way to use NativeJSON.stringify(). I ended up building the json string "by hand", i.e Iterating on the property ids of the native javascript object and adding these properties to a java Map.
The Map is then transformed into a json object using new JSONObject(map);
link to source code
https://gist.github.com/eleco/c2bc77dca9ecc844a924

Categories

Resources