Is there a way to get and use an extended Java class from JavaScript (Nashorn)?
What I am trying to do is to extend a Java class in Nashorn and then pass it back to Java. For example, we define a class which extends another class in JS:
var SomeClass= Java.type("com.test.SomeClass");
var MySomeClass = Java.extend(SomeClass, {
aMethod1: function() {
print("m1");
},
aMethod2: function() {
print("m2");
},
});
Then I've tried to get this extended object in Java like so: Object mySomeClass = scriptEngine.get("MySomeClass");
However, I am unable to make any method calls on this object in Java. It always throws an exception when I am trying to cast it to a base class - com.test.SomeClass.
Is it possible to use an extended class created in Nashorn (JS) in Java?
Here is a working example extending ArrayList:
String script = "(function() {\n" +
" var ArrayList = Java.type('java.util.ArrayList')\n" +
" var ArrayListExtender = Java.extend(ArrayList)\n" +
" var list = new ArrayListExtender() {\n" +
" get: function(idx) {\n" +
" return idx >= list.size() ? " +
"'no such value' : Java.super(list).get(idx);\n" +
" }\n" +
" }\n" +
" return list;\n" +
"} ());";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
#SuppressWarnings("unchecked")
List<String> result = (List<String>) engine.eval(script);
result.add("gotcha");
System.out.println(result.get(0));
System.out.println(result.get(10));
You don't state what exception you are getting but the problem is likely in how you consume or use the type. Note that neither Java.type nor Java.extend return a java.lang.Class - these methods return an internal Nashorn type with an undocumented API.
Related
I'm working on developping a webservice that communicate two application with each other, the first application will send a json object to the second one.
I'm stuck in translating this json object :
$body = "{"fields":{"project":{"key":"'+$projectKey+'"}
,"issuetype":{"name": "'+$issueType+'"}
,"summary":"'+$summary+'"
,"description":"'+$description+'"
,"customfield_12721":"'+$FirstName+'"
,"customfield_12722":"'+$LastName+'"
,"customfield_12723":{"value":"'+$EmployeeCategory+'"}
,"customfield_12732":"'+$Externalfunction+'"
,"customfield_12725":"'+$CorporateID+'"
,"customfield_12726":{"value":"'+$VermegCompany+'"}
,"customfield_12685":{"value":"'+$IndusRegion+'"}
,"customfield_12673":{"value":"'+$Product+'"}
,"customfield_12727":{"value":"'+$Profile+'"}
,"customfield_12667":{"name":"'+$Manager+'"}
,"customfield_12708":"'+$BeginDate+'"
,"customfield_14000":"'+$Reglementation+'"
,"customfield_14001":"'+$Department+'"
,"customfield_14002":"'+$SubDepartment+'"}
}";
To a String variable like :
String json = "{fields:{project:{\"key\":\""+ projectkey +"}"
+ "\",\"issuetype\":\""
+ "\",\"customfield_12721\":\"" + employee.getFirstName()
+ "\",\"description\":\"" + description
+ "\",\"summary\":\"" + summary
+ "\",\"customfield_12722\":\""+ employee.getLastName()
+ "\",\"customfield_12732\":\"" + employee.getFte()
+ "\",\"customfield_14000\":\"" + employee.getReglementation()
+ "\",\"customfield_14001\":\"" + employee.getDepartment()
+ "\",\"customfield_14002\":\"" + employee.getSubdepartment()
+ "\",\"fulltime\":" + Math.round(Double.parseDouble(employee.getFulltime().replaceAll(",",".")))
//+ ",\"email\":\"" + employee.getEmail()
+ ",\"citizenship\":\"" + employee.getCitizenship()
+ "\",\"gnn\":\""+ employee.getGnn()
+ "\",\"company\":\"" + employee.getCompany()
+ "\",\"employeeid\":\"" + employee.getEmployeeid()
+ "\",\"customfield_12708\":\"" + employee.getStartdate()
//+ "\",\"enddate\":\"" //+ employee.getEnddate()
+ "\",\"product\":\"" + employee.getProduct()
+ "\",\"customfield_12725\":\"" + employee.getInternalnumber()
// + "\",\"employeeid\":\"" + employee.getEmployeeid()
+ "\"}}";
Can you please help ?
Here you can use JsonConvert library to get json data.bellow are the example how to use it
String json = "{\"FirstName\":\"Jack\",\"LastName\":\"Tor\"}";
var data = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
Console.WriteLine(data);
Console.ReadLine();
Using a web tool
If you just want to convert it fast into a string, you can use a Json to String converter on the web.
Using a library
If you however want a good solution in you applications, a way to solve this problem would be to serialize and parse using a library such as GSON. This is of course, if you don't intend to create this conversion yourself.
GSON is quite easy to use and takes care of the translation for you. Se example below:
The sending application:
Gson gson = new Gson();
String jsonStr = gson.toJson(employee); // Serialize (from Java class to JSON string)
// Send data (jsonStr) ...
The receiving application:
// Receive data (jsonStr) ...
Gson gson = new Gson();
Employee employee = gson.fromJson(jsonStr); // Parse (from JSON string to Java class)
GSON will by default name the fields the same as the member variable names in the Java class. If you need to change the field names in the JSON string, you can use #SerializedName("newName") in front of the member variables.
Example:
class Employee {
...
#SerializedName("customfield_12721") String firstName;
...
}
I have a JSONObject that is similar to something like this:
{
"category":"abc"
"staus":""open"
"external":[
{"name":"123", "type":"OTHER"},
{"name":"678", "type":"ALPHA"},
{"name":"890", "type":"DELTA"}
]
}
If I want to use JSONAssert to check if the item {"name":"678"} exists and I don't know the item's order and the number of items in the "external" array, how should I do in Java?
It seems the ArrayValueMatcher should be the way to go but I just cannot get it works.
Please help
You could use JsonPath for this usecase :
JSONArray array = JsonPath.read(json, "$.external[?(#.name == '678')]");
Assertions.assertThat(array).hasSize(1);
Here is a complete example using JsonAssert:
#Test
public void foo() throws Exception {
String jsonString = "{\n" +
" \"category\":\"abc\",\n" +
" \"staus\":\"open\",\n" +
" \"external\":[\n" +
" {\"name\":\"123\", \"type\":\"OTHER\"},\n" +
" {\"name\":\"678\", \"type\":\"ALPHA\"},\n" +
" {\"name\":\"890\", \"type\":\"DELTA\"}\n" +
" ]\n" +
"}";
JsonAssert.with(jsonString).assertThat("$.external[*].name", hasItem(equalTo("678")));
}
I am trying to work with Selenium in Java with Angular 5 based website.
Selenium does not support it directly, but JavascriptExecutor can help validating the page components finished loading.
The problem is, I do not know how to implement the JavaScript to validate this.
I am using:
return window.getAngularTestability === undefined
to validate Angular 5 exists in the current page, but the next part of the implementation is a mystery to me.
I know I have to use return window.getAngularTestability somehow.
You can create a generic java method for running any javascript within your Java code. Refer below block of code:-
public void executeJavascript(String script) {
((JavascriptExecutor) driver).executeScript(script);
}
You can pass your return javascript statements as parameters to this method.
I found an answer after a lot of research and searching the web.
The solution is not mine, so i do not deserve the credit.
ExpectedCondition<Boolean> expectation = driver -> ((JavascriptExecutor) driver).executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"if (document.readyState !== 'complete') {" +
" callback('document not ready');" +
"} else {" +
" try {" +
" var testabilities = window.getAllAngularTestabilities();" +
" var count = testabilities.length;" +
" var decrement = function() {" +
" count--;" +
" if (count === 0) {" +
" callback('complete');" +
" }" +
" };" +
" testabilities.forEach(function(testability) {" +
" testability.whenStable(decrement);" +
" });" +
" } catch (err) {" +
" callback(err.message);" +
" }" +
"}"
).toString().equals("complete");
try {
WebDriverWait wait = new WebDriverWait(webDriver(), 15);
wait.until(expectation);
} catch (Throwable error) {
new Exception("Timeout waiting for Page Load Request to complete.");
}
I've got an application that executes a lot of javascript server side and I'm trying to convert from Rhino to Nashorn but I am running into trouble with my scripts. Using Rhino I would always convert whatever arguments I had for a function into a JSON string but that's really slow. With Nashorn I'm trying to just pass in the arguments as Java objects but they don't seem to inherit functions from Javascript's Object type. Here is a sample method that illustrates my problem where hasOwnProperty is not available on my array:
public String printArrayValues() throws ScriptException, NoSuchMethodException {
String script =
"function printArrayValues(objArray) {\n" +
" var result = '';\n" +
" for(var obj in objArray) {\n" +
" if(objArray.hasOwnProperty(obj)) {\n" +
" result = result + ' ' + objArray[obj];\n" +
" }\n" +
" }\n" +
" return result;\n" +
"}";
List<String> data = Arrays.asList(new String[]{ "one", "two", "three"});
ScriptEngine scriptEngine = new NashornScriptEngineFactory().getScriptEngine();
scriptEngine.eval(script);
String result = (String) ((Invocable) scriptEngine).invokeFunction("printArrayValues", data);
}
Here the call to invokeFunction throws an exception:
javax.script.ScriptException: TypeError: [one, two, three] has no such function "hasOwnProperty" in <eval> at line number 4
If I call the same function in a browser, I get what I would expect:
> printArrayValues(["one", "two", "three"]);
> " one two three"
Is there some way I can accomplish this so I really can use these Java objects without turning them into a JSON string and then eval'ing it into a Javascript object?
You can't use Java arrays in this way. Java arrays are "hardwired" objects. Unlike ordinary objects, they don't have methods and they support the [] operator, which objects can't.
This Article about Nashorn at Oracle explains that you need to use the Java.to and Java.from methods in your javascript in order to change a Java array to a Javascript array.
use Java.from() to transform the Java List to Javascript Array, and then operate on it.
String script =
"function printArrayValues(objArray) {\n" +
" var result = '';\n var temp = Java.from(objArray);" +
" for(var obj in temp ) {\n" +
" if(temp .hasOwnProperty(obj)) {\n" +
" result = result + ' ' + temp [obj];\n" +
" }\n" +
" }\n" +
" return result;\n" +
"}";
Basically I want to be able to do be able to parse a JavaScript-like syntax. For example:
var results = system.function('example');
if(results == "hello") {
console_print("ding dong.");
}
So basically you get it, results will be a "string" and system.function will call a Java method, and those results will go into the results string.
I want to be able to do basic math, and validation in this as well, but I want this done in Java. Any ideas?
If you're willing to use JavaScript (not just JavaScript-like), and you're using Java 1.6+, then you can use the Scripting API:
import javax.script.*;
public class Main {
public static void main (String[] args) throws Exception {
String source =
"var system = new Array(); \n" +
"system['foo'] = function(s) { return 'hello'; } \n" +
" \n" +
"var results = system.foo('example'); \n" +
" \n" +
"if(results == \"hello\") { \n" +
" print(\"ding dong.\"); \n" +
"} \n";
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript");
engine.eval(source);
}
}
which will print:
ding dong.
to the console.
And to invoke Java methods from the scripting language, you could give BeanShell a try:
package test;
import bsh.Interpreter;
public class Main {
public static void foo(Object param) {
System.out.println("From Java, param=" + param);
}
public static void main (String[] args) throws Exception {
Interpreter i = new Interpreter();
i.eval("a = 3 * 5; test.Main.foo(a);");
}
}
which will print:
From Java, param=15
The core interpreter JAR of BeanShell is only 143 KB: more lightweight than that will be difficult, IMO.
I think you can use Rhino for this. Rhino is an javascript engine that can be embedded in java.