Jdk nashorn api ScriptEngine returning undefined - javascript

I am trying to run a simple javascript function using jdk nashorn script engine. There I am returning one of the passed variable to the function just for simplicity to check if nashorn api working fine or not. After script engine runs the function I get undefined as result.
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import javax.script.ScriptException;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
public class JDKNashornScriptRunner {
protected static final ScriptEngine scriptEngine = new NashornScriptEngineFactory().getScriptEngine();
protected final Bindings bindings;
public JDKNashornScriptRunner(String script)
{
bindings = scriptEngine.createBindings();
try
{
scriptEngine.eval(script, bindings);
}
catch( ScriptException e )
{
throw new RuntimeException("Exception while compiling script", e);
}
}
public Object runScript( String v1, String v2, String v3, String v4)
throws NoSuchMethodException, ScriptException
{
bindings.clear();
bindings.put("v1", v1);
bindings.put("v2", v2);
bindings.put("v3", v3);
bindings.put("v4", v4);
return ((ScriptObjectMirror) bindings.get("myFunction")).call(null);
}
}
Main program here -
public class RunMyFunctionScript {
public static void main(String args[])
{
String runScript = "function myFunction(v1, v2, v3, v4) {return v3;}";
JDKNashornScriptRunner scriptRunner = new JDKNashornScriptRunner(runScript);
Object result = scriptRunner.runScript("a","b","c","d"); //Here I am getting undefined as value
String v3Value = String.valueOf(result);
System.out.println(v3Value);
}
}
So the problem here is when scriptEngine is trying to run the script using below given line, I am getting undefined (it should give me c as value)
((ScriptObjectMirror) bindings.get("myFunction")).call(null);

Because you're not passing any argument to your JavaScript function. runScript should be:
public Object runScript( String v1, String v2, String v3, String v4)
throws NoSuchMethodException, ScriptException
{
return ((JSObject) bindings.get("myFunction")).call(null, v1, v2, v3, v4);
}

Related

Call Wicket 6 Code from Javascript and return value

I have managed to call my Wicket 6 Java code from Javascript using option A in this example: https://stackoverflow.com/a/42612027/1047418
However, I have not been able to find examples for returning data from the Java side back to JavaScript (the generated JavaScript callback function does not even include a return statement). How can this be achieved?
Edit: I am not trying to set an attribute in Java and as I've already explained, calling Wicket from JavaScript is not the problem here. I am trying to return a JSON object from Wicket back to the browser as a result of an Ajax request.
Edit2: Following martin-g's examples I cobbled up this working example...
Java
public class MyAjaxBehaviour extends AbstractDefaultAjaxBehavior {
#Override
protected void onComponentTag(ComponentTag tag) {
super.onComponentTag(tag);
tag.put("aprachatcallbackurl", getCallbackUrl());
}
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
attributes.setDataType("json");
attributes.setWicketAjaxResponse(false);
}
#Override
protected void respond(AjaxRequestTarget target) {
getComponent().getRequestCycle().replaceAllRequestHandlers(
new TextRequestHandler("application/json", "UTF-8", "{...JSON GOES HERE...}));
}
}
JavaScript
var mySuccessCallback = function(param1, param2, data, statusText) {
// Data contains the parsed JSON object from MyAjaxBehaviour.respond(...)
...
}
var myFailureCallback = function() {
...
}
Wicket.Ajax.get({
"u": callbackUrl,
"dt": "json",
"wr": false,
"sh": [mySuccessCallback],
"fh": [myFailureCallback]
});
Main problem as that the Wicket 7 Reference incorrectly instructs to use "wr" instead of "dt" in the JavaScript call. :)
I think you can do it in a simpler way!
Wicket Ajax API is just: Wicket.Ajax.ajax({...}). All you need to prepare at the server side is to save the callback url, e.g. by saving it globally in the window object or in HTML element's attributes (data-the-url).
public class CallFromJavascriptBehavior extends AbstractDefaultAjaxBehavior {
#Override
protected void respond(AjaxRequestTarget target) {
final StringValue parameterValue = RequestCycle.get().getRequest().getQueryParameters().getParameterValue("yourName");
System.out.println(String.format("Hello %s", parameterValue.toString()));
// write anything to the WebResponse and then consume it in the JS success handler. See below
}
#Override
public void onComponenntTag(ComponenntTag tag, Component component) {
super.onComponenntTag(tag, component);
tag.put("data-the-url", getCallbackUrl());
}
}
Then in your JS code you can do:
var callbackUrl = jQuery("#theElementId").data("the-url");
Wicket.Ajax.get({"u": callbackUrl, "sh":[successHandler], "fh": [failureHandler] });
Where successHandler and failureHandler are JS functions defined inline (e.g. function(...) {}) or elsewhere.
More documentation you can find at:
https://ci.apache.org/projects/wicket/guide/7.x/single.html#_ajax_request_attributes_and_call_listeners
A blog article with an complete example at http://wicketinaction.com/2012/07/wicket-6-javascript-improvements/
You can just write a Resource and mount it, and get it with your favorite Ajax-approach.
For example:
public class MyResource extends AbstractResource
#Override
protected ResourceResponse newResourceResponse( Attributes attributes )
{
ResourceResponse resourceResponse = new ResourceResponse();
resourceResponse.setContentType( "text/json" );
resourceResponse.setTextEncoding( "utf-8" );
HttpServletRequest request = (HttpServletRequest) attributes.getRequest().getContainerRequest();
try
{
this.json = IOUtils.toString( request.getInputStream() );
}
catch ( IOException e )
{
e.printStackTrace();
}
resourceResponse.setWriteCallback( new WriteCallback()
{
#Override
public void writeData( Attributes attributes ) throws IOException
{
OutputStream outputStream = attributes.getResponse().getOutputStream();
Writer writer = new OutputStreamWriter( outputStream );
writer.write( MyResource.this.json );
writer.close();
}
} );
return resourceResponse;
}
(Copied from my other answer here https://stackoverflow.com/a/17876029/461499)
And see here for mounting it:
https://dzone.com/articles/how-implement-rss-feeds-custom

Passing dates between c# and javascript

I have an ASP.Net MVC app that returns a view model, which when converted to JSON using system.web.mvc.jsonresult looks as follows:
On the client I'm using KnockoutJS. I use MomentJS to format the value for the VoucherDate so that it can be displayed for humans:
var recsArray = [];
$.each(data.Vouchers, function (key, value) {
recsArray.push(
new edited(
interchangeId,
value.SupplierIsValid,
value.VoucherNo,
value.LegacySupplierId,
value.Transactions,
moment(value.OriginalVoucher.VoucherDate).format('YYYY/MM/DD HH:mm'),
value.OriginalVoucher
)
);
As you can see from the previous code snippet, in addition to pushing the data into an observable array for display in a KOGrid, I also push the entire "OriginalVoucher". This enables the user to edit the value for "LegacySupplierId" and click "Resubmit" which posts back the entire view model as seen below:
self.resubmit = function () {
var data = {
Vouchers: ko.toJS(this.recs),
BatchId: self.batchId(),
InterchangeId: interchangeId,
IsReadWrite: self.isReadWrite,
Interface: self.interface,
ReportClient: self.reportClient
};
$.ajax({
type: "POST",
url: BASE_URL + 'EditBatch/ResubmitRejectedVouchersAsNewBatch',
data: ko.toJSON(data),
I've checked using Fiddler and confirmed that the VoucherDate fields contain values such as /Date(14543712000000)/. My problem is, when reading in the C# controller, all of the dates are presented as 1/1/0001 12:00:00 AM.
I've read that javascript has a date.toISOString() function to convert to a format that C# will be happy with. Is there a better way that trying to find each date field in the javascript view model and executing a conversion against each before posting back to the C# controller
I think I have previously solved this problem using automapper by following instructions here: enter link description here
In my source code I can see that I created the following class but I don't know how / if this gets used:
public class JsonDateTimeTypeConvertor : ITypeConverter<string, DateTime>
{
public DateTime Convert(ResolutionContext context)
{
string jsonDate = context.SourceValue.ToString();
string offsetAsString = Regex.Match(jsonDate, #"\d+").Value;
double offset = System.Convert.ToDouble(offsetAsString);
DateTime ret = DataUtils.ConvertFromUnixTimestamp(offset);
return ret;
}
}
I think it was being used but I've inadvertently disabled it. This probably happened when I changed the type for the ViewModel received by the controller. The previous version of the application worked, so I guess the datetime was being converted correctly - probably by this automapper extension. I have pulled the old version of the code from TFS but can't figure out how it's doing the magic - I wish I'd made better notes at the time! All I have is:
I needed to create a custom type convertor to deal with JSON dates being passed to the EditBatch controller in the format of number of milliseconds since 1900.
I followed the wiki documentation from here:
https://github.com/AutoMapper/AutoMapper/wiki/Custom-type-converters
May be you can use a regex pattern like this. The following code shows ToJavaScriptDate() function that does this for you:
function ToJavaScriptDate(value)
{
var pattern = /Date\(([^)]+)\)/;
var results = pattern.exec(value);
var dt = new Date(parseFloat(results[1]));
return (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();
}
The ToJavaScriptDate() function accepts a value in /Date(ticks)/ format and returns a date string in MM/dd/yyyy format. Inside, the ToJavaScriptDate() function uses a regular expression that represents a pattern /Date(([^)]+))/.
The exec() method accepts the source date value and tests for a match in the value. The return value of exec() is an array. In this case the second element of the results array (results[1]) holds the ticks part of the source date. For example, if the source value is /Date(836418600000)/ then results[1] will be 836418600000. Based on this ticks value a JavaScript Date object is formed. The Date object has a constructor that accepts the number of milliseconds since 1 January 1970. Thus dt holds a valid JavaScript Date object. The ToJavaScriptDate() function then formats the date as MM/dd/yyyy and returns to the caller.
I'm really not a fan of the way the default JavaScriptConverter handles dates. I use the following class. (The CustomString class is kind of a dirty hack to get around the fact that the Serialize command is expected to return an IDictionary. See blog post here: http://blog.calyptus.eu/seb/2011/12/custom-datetime-json-serialization/)
public class DateTimeJsonSerializer : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (dictionary == null)
return null;
return new JavaScriptSerializer().ConvertToType(dictionary, type);
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
if (!(obj is DateTime)) return null;
return new CustomString(((DateTime) obj).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"));
}
public override IEnumerable<Type> SupportedTypes
{
get { return new ReadOnlyCollection<Type>(new List<Type> { typeof(DateTime), typeof(DateTime?) }); }
}
}
public class CustomString : Uri, IDictionary<string, object>
{
public CustomString(string str) : base(str, UriKind.Relative)
{}
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public void Clear()
{
throw new NotImplementedException();
}
public bool Contains(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
public bool Remove(KeyValuePair<string, object> item)
{
throw new NotImplementedException();
}
public int Count { get; private set; }
public bool IsReadOnly { get; private set; }
public bool ContainsKey(string key)
{
throw new NotImplementedException();
}
public void Add(string key, object value)
{
throw new NotImplementedException();
}
public bool Remove(string key)
{
throw new NotImplementedException();
}
public bool TryGetValue(string key, out object value)
{
throw new NotImplementedException();
}
public object this[string key]
{
get { throw new NotImplementedException(); }
set { throw new NotImplementedException(); }
}
public ICollection<string> Keys { get; private set; }
public ICollection<object> Values { get; private set; }
}
To use this in asp.net, I have the following section in my web.config (make sure to replace "AssemblyNameGoesHere" with the assembly that contains the converter class:
<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483644">
<converters>
<add name="DateTimeConverter" type="DateTimeJsonSerializer,AssemblyNameGoesHere" />
</converters>
</jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>
I managed to work-around this by changing the data type on the "VoucherDate" field in the C# model from DateTime to String. I don't currently understand why this works. I also think there must be a better way!?

Invoking function on Java subclass in Nashorn

I have a JavaScript script that looks something like this:
function run(database) {
var result = database.query("query", "some resource name");
//operations on result
return result;
}
and I have Java code that executes the script that is something like this:
public Object execute(String script, Database database) {
NashornScriptEngineFactory nsef = new NashornScriptEngineFactory();
ScriptEngine engine = nsef.getScriptEngine();
try {
engine.eval(script);
Invocable invocable = (Invocable) engine;
return invocable.invokeFunction("run", database);
} catch(ScriptException e) {
throw new RuntimeException(e);
}
}
Database is an interface which contains several method definitions, but does not contain the query method. I am calling execute with an implementation of Database, call it DatabaseImpl, that does have the query method. This will be polymorphic, and the script is expected to know what methods are available on the Database instance passed to it. I decided against using generics with this since they are erased at runtime and so the JavaScript has no way of using them anyway, so it's up to the script writer to get the types right.
However, when I run this code, I get the following exception:
javax.script.ScriptException: TypeError: database.query is not a function in <eval> at line number 25
Basically, the gist is, I have an object which implements an interface, and call a method that the particular instance implements, but is not part of the interface definition. My impression is that this should still work, but it does not. It doesn't make much sense to me that I would need to subcast within the script to have access to the query method (is that even possible?), so why am I getting this error? Is it because the method isn't available from the interface definition? Is there a workaround?
Thanks.
This is the main class:
package so;
import java.io.InputStreamReader;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
public class Nashorn {
public static void main(String[] args) {
try (InputStreamReader in = resource()) {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(in);
Invocable invocable = (Invocable) engine;
Database database = new DatabaseImpl();
Object x = invocable.invokeFunction("run", database);
System.out.println(x);
} catch (Exception e) {
e.printStackTrace();
}
}
private static InputStreamReader resource() throws Exception {
return new InputStreamReader(Nashorn.class.getResourceAsStream("db.js"), "utf-8");
}
}
Interface and implementation
package so;
public interface Database {
void connect();
}
package so;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class DatabaseImpl implements Database {
#Override
public void connect() {
System.out.println("Connecting");
}
public List<?> query(String ... stmt){
List<String> lst = new ArrayList<>();
lst.addAll(Arrays.asList(stmt));
lst.addAll(Arrays.asList("A","B","C"));
return lst;
}
}
The javascript file (so/db.js)
function run(database) {
var result = database.query("query", "some resource name");
//operations on result
return result;
}
Running results in:
[query, some resource name, A, B, C]
It basically works.

Asynchronous JSON String loading JavaScript

I'm using the d3 force directed graph to display some data I get from an API. Before I can display it, it runs through a java class, which does write it into the right json format.
Since the programm runs in a JavaFX WebView I have a bridge class, that does have a getter method I can call from the JavaScript.
In my Main class I create a WebView and assign the bridge to it. I initialize my JSON translator and pass the bridge to it.
#Override
public void start(Stage stage) {
try {
new JsonTranslator(individual, depth, bridge);
Scene scene = createScene();
[...]
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
private Scene createScene() {
[...]
try {
JSObject jsobj = (JSObject) webEngine.executeScript("window");
jsobj.setMember("java", bridge);
} catch (Exception e) {
e.printStackTrace();
}
[...]
}
In my JSONTranslator class I write the json and pass it to the bridge
private void writeFile() {
try {
bridge.setJSONObject(obj.toJSONString());
FileWriter file = new FileWriter(
"C://path/to/some/file.json"
file.write(obj.toJSONString());
file.flush();
file.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("Object:" + obj);
}
I also write it to a file. The data gets printed as expected. Now in my bridge the data is available throughout the getter / setter. In JSToFXBridge.java:
public String getJSONObject(){
System.out.println("get request: " + json);
return json;
}
public void setJSONObject(String string){
this.json = string;
}
Now I call it from my JavaScript
[...]
var draw = function(json, callback) {
[...]
callback.call(data);
};
var data = java.getJSONObject();
draw(data);
However it does print get request: -my json data- on the console, the json string is compleatly fine. If I copy & paste it from the console to be like this var data = -my json data- in the code it works. Only to asign it directly from the method won't work. I can't figure out why since I try to load it asynchronously. Based on this tutorial. Do I make a mistake in laoding the string? Or is it even a wrong way to do so?
Good answer / tutorial to asynchronous JavaScript callbacks can be found here. Solution, which created a new problem [ solved as well ], provided here.
In general think of this pattern:
function addOne(thenRunThisFunction) {
waitAMinuteAsync(function waitedAMinute() {
thenRunThisFunction()
})
}
addOne(function thisGetsRunAfterAddOneFinishes(){})
Explains it very well

Android Phonegap - Passing JSONObject to javascript

I'm trying to pass some information from an Android native class to the javascript.
I'm taking a bundle, converts it to JSONObject and passing the string representation of it.
But when trying to parse it in the JS, it fails.
This is what I do:
JSONObject jsonObject = new JSONObject();
for (String key : bundle.keySet()) {
Object value = bundle.get(key);
try {
jsonObject.put(key, value.toString());
} catch (JSONException e) {
// Do nothing
}
}
final String jsStatement = String.format(
"window.doSomething('%s');", jsonObject.toString());
cordova.getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
webView.loadUrl("javascript:" + jsStatement);
}
});
Can you tell me why it's not being parsed in the JS and how can I solve it?
Look into the Javascript Interface annotation which can be used to pass in / access the values from Java directly

Categories

Resources