I am stuck with loading java functions so that it can be called from lua file using luaj.
What i currently do is create something like this :
in some_package/aif.java :
package some_package;
public class aif extends TwoArgFunction {
public aif() {
}
#Override
public LuaValue call(LuaValue modname, LuaValue env) {
LuaValue library = tableOf();
library.set("foo", new foo());
env.set("aif", library);
return library;
}
//the rest contains the implementations of java functions
}
and then in lua file :
require "some_package/aif"
--etc ...
and then in Main.java file :
public static void Main(String[] args) {
String script = "lib/some_lua_file.lua";
globals = JsePlatform.standardGlobals();
LuaValue chunk = globals.loadFile(script);
chunk.call( LuaValue.valueOf(script) );
}
this code works , but what i want is that in lua file we dont have to use "require". I have achieved this similarly but in c++ using this line :
luaL_requiref(L, "aif", luaopen_aiflib, 1);
can we do like that in luaj? i tried :
globals.load(new aif());
but gets Exception in thread "main" org.luaj.vm2.LuaError: index expected, got nil (variable env in call function of aif class is nil)
anybody knows how to setup aif as lua libary to use with luaj?
You can write your MyXArgImpl like the following:
package mypackage;
import org.luaj.vm2.LuaValue;
import org.luaj.vm2.lib.ZeroArgFunction;
public class MyZeroArgImpl extends ZeroArgFunction {
public LuaValue call() {
return valueOf("My Zero Arg Implementation");
}
}
and then add it to your LUA as the following:
LuaValue globals = JsePlatform.standardGlobals();
globals.get("dofile").call( LuaValue.valueOf(yourScriptfile));
globals.set("callMyFunction", new MyZeroArgImpl());
Now you can call your function inside your LUA script even without require('...'):
print(callMyFunction())
I have found the answer after looking at the luaj implementation of Lua library.
i modified my code :
package some_package;
public class aif extends OneArgFunction{
public aif() {
}
#Override
public LuaValue call(LuaValue env) {
Globals globals = env.checkglobals();
LuaTable aif = new LuaTable();
aif.set("foo", new foo());
env.set("aif", aif);
globals.package_.loaded.set("aif", aif);
return aif;
}
//the rest contains the implementations of java functions
}
I code the aif class to TwoArgFunction is because the tutorial said to do so. Now with the above code, no need to require the class in lua file
Lets say your script that you are loading has a function "receive_aif"
function receive_aif( aifObj )
--This is how you can invoke public function associated with aifObj
aifObj:someAifFunction()
end
From java, you can pass aif instance as: (This should work with any java object )
aif aifObj = new aif()
LuaValue receive_aif_handle = globals.get("receive_aif");
LuaValue retvals = receive_aif_handle.call( CoerceJavaToLua.coerce( aifObj ) );
I am using similar constructs in my application using "3.0 aplha-2" release
Related
I am learning GWT, I am trying following example in which I have tried to pass the JSON object in java function.
public class HomeController implements EntryPoint {
public void onModuleLoad() {
createTestNativeFunction();
Presenter presenter = new PersenterImpl();
presenter.go(RootPanel.get());
}
public native void createTestNativeFunction()/*-{
parser: function() {
var that = this;
var jsonResult = JSON.parse({id:42,name:'yo'});
return this.#com.easylearntutorial.gwt.client.HomeController::onParse(Lorg/sgx/jsutil/client/JsObject;)(jsonResult);
}
void onParse(jsonResult){
System.out.println(jsonResult);
}
}
}-*/;
}
I am getting following errors:
Tracing compile failure path for type 'com.easylearntutorial.gwt.client.HomeController'
[ERROR] Errors in 'file:/C:/Users/ameen/workspace/Tutorial/src/com/easylearntutorial/gwt/client/HomeController.java'
[ERROR] Line 31: missing ; before statement
void onParse(jsonResult){
--------------------------------^
[ERROR] Hint: Check the inheritance chain from your module; it may not be inheriting a required module or a module may not be adding its source path entries properly
[WARN] Server class 'com.google.gwt.dev.shell.jetty.JDBCUnloader' could not be found in the web app, but was found on the system classpath
[WARN] Adding classpath entry 'file:/C:/Program%20Files/gwt-2.7.0/gwt-dev.jar' to the web app classpath for this session
For additional info see: file:/C:/Program%20Files/gwt-2.7.0/doc/helpInfo/webAppClassPath.html
You really should try to avoid JSNI. You can probably write 99% of your code not using JSNI at all. If you really need it, you should use the new JsInterop instead, documentation still in early stage but you can see this documentation here.
If you need to use JsInterop or JSNI it is usually because you need to wrap a JS lib, so first, try to find if it is already wrapped. If it is not you can always use some other wrapper library to learn how to wrap your JS lib.
OpenLayers JsInterop wrapper https://github.com/TDesjardins/gwt-ol3
OpenLayers JSNI wrapper (deprecated) https://github.com/geosdi/GWT-OpenLayers
Or explore github https://github.com/search?q=topic%3Agwt+topic%3Ajsinterop
System.out.println() is a java function, you are looking for console.log().
The body of the native is JavaScript, not Java.
You are declare you variable jsonResult into your parser: function(), jsonResult only exist into that function. Thats why the system say you that
missing ; before statement
Because you never declare the varieble into createTestNativeFunction().
Plus sjakubowski is right System.out.println() is a java function, you need to use console.log() on JavaScript.
Try this:
public native void createTestNativeFunction(){
var jsonResult = {};
parser: function() {
var that = this;
jsonResult = JSON.parse({id:42,name:'yo'});
return this.#com.easylearntutorial.gwt.client.HomeController::onParse(Lorg/sgx/jsutil/client/JsObject;)(jsonResult);
}
void onParse(jsonResult){
console.log(jsonResult);
}
}
I did the following to solve my errors.
public class HomeController implements EntryPoint {
public void onModuleLoad() {
createTestNativeFunction();
Presenter presenter = new PersenterImpl();
presenter.go(RootPanel.get());
}
// var jsonResult = JSON.parse({id:42,name:'yo'});
public native void createTestNativeFunction()/*-{
var that = this;
$wnd.testFunction = function(jsonResult) {
that.#com.easylearntutorial.gwt.client.HomeController::onParse(Lorg/sgx/jsutil/client/JsObject;)(jsonResult);
};
}-*/;
public void onParse(JsObject jsonResult){
int i =42;
}
}
I have a Java program which accepts some string input in the below format:
setData("hello")
Also, I have a groovy script say "sample.groovy", it is a groovy file in the following sample format:
class sample
{
def doOperation()
{
println("Inside doOperation()")
}
def setData(String str)
{
println("Incoming data : " + str)
}
}
From the Java class, create an object of above groovy class named : sampleObj.
I have to invoke sampleObj.setData("hello") from my Java application using the input string say "setData("hello")".
Then how can I invoke this method?
This is exactly the kind of problem that GroovyShell solves.
Here's an example:
import groovy.transform.Canonical
import org.codehaus.groovy.control.CompilerConfiguration
#Canonical
class ScriptState {
String data
}
abstract class MyScript extends Script {
void setData(String data) {
binding.state.data = data
}
}
def state = new ScriptState()
def cc = new CompilerConfiguration(scriptBaseClass: MyScript.class.name)
def shell = new GroovyShell(MyScript.classLoader, new Binding(state: state), cc)
shell.evaluate('println "Running script"; setData "The Data"')
assert state.data == 'The Data'
println state
Running this will print:
Running script
ScriptState(The Data)
I based this example on the Groovy Goodness example.
Normally, you don't need to set the classloader as I did in MyScript.classLoader... I only needed to do this because I ran this as a script, to the script class would not be visible to the GroovyShell's script classloader if I didn't do that.
EDIT
After the question was heavily edited, it seem the problem is that you don't know which class the Java object to call from the script will have.
In that case, just change the MyScript class to do something like this:
abstract class MyScript extends Script {
def methodMissing(String name, args) {
// this will call any method called inside the script
// on the sample Object
binding.sampleObject."$name"(*args)
}
}
Now, when creating the GroovyShell:
def shell = new GroovyShell(
MyScript.classLoader,
new Binding(sampleObject: new Sample()),
cc)
Running this code:
shell.evaluate('doOperation(); setData "The Data"')
will print the expected:
Inside doOperation()
Incoming data : The Data
I am currently executing my JavaScript-scripts with this java code:
ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
engine.eval(new FileReader("awesome_script.js"));
I need to call Java functions from JavaScript, so I defined this at the top of my awesome_script.js file:
var first = Java.type('io.github.awesomeprogram.FirstClass');
var second = Java.type('io.github.awesomeprogram.SecondClass');
var extra = Java.type('io.github.awesomeprogram.ExtraClass');
I can then call some methods from these classes, e.g.:
second.coolmethod("arg1",2);
My problem is now that I need to use many java classes inside of my scripts. I also have a lot of scripts and I think it is very inefficient to define every single one of this classes in every script.
So I am looking for a solution to create the objects created inside of JavaScript with Java.type() inside of Java and then pass them to the script I want to execute.
How can I do this?
Thanks in advance!
You may want to avoid using the "internal" classes in packages like "jdk.internal.", "jdk.nashorn.internal.". In jdk9, dynalink is an API ("jdk.dynalink" has exported packages). In jdk9, you can call jdk.dyanlink.beans.StaticClass.forClass(Class) [ http://download.java.net/java/jdk9/docs/jdk/api/dynalink/jdk/dynalink/beans/StaticClass.html#forClass-java.lang.Class- ] to construct "type" objects and expose those as global variables to the script engine. For jdk8, you could pre-eval a script that uses Java.type(String) calls before evaluating "user" scripts. You can also call "Java.type" function from Java code.
Solution for jdk9:
import jdk.dynalink.beans.StaticClass;
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
e.put("AList", StaticClass.forClass(java.util.ArrayList.class));
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
Solution for jdk8:
import javax.script.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// eval a "boot script" before evaluating user script
// Note that this script could come from your app resource URL
e.eval("var AList = Java.type('java.util.ArrayList')");
// now evaluate user script!
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
Alternative solution for jdk8:
import javax.script.*;
import jdk.nashorn.api.scripting.*;
public class Main {
public static void main(String[] args) throws Exception {
ScriptEngineManager m = new ScriptEngineManager();
ScriptEngine e = m.getEngineByName("nashorn");
// get Java.type function as object
JSObject javaTypeFunc = (JSObject) e.eval("Java.type");
// you can javaTypeFunc from java code many times
Object alType = javaTypeFunc.call(null, "java.util.ArrayList");
// expose that as global
e.put("AList", alType);
// now evaluate user script!
e.eval("var al = new AList(); al.add('hello'), al.add('world')");
e.eval("print(al)");
}
}
After quite a bit of research I found a way to put global variables in the ScriptEngine before executing: The Java Scripting API (Oracle Docs)
This enabled me to put any object I want into a global variable. However, I still needed a way to get the Object that Java.type() creates inside of Java. So I wrote a test script which returns one of this objects and I found out it is an object of the type jdk.internal.dynalink.beans.StaticClass. This class has an constructor which takes a ordinary Class as an argument. Sadly, this constructor is not usable in my code because it is not visible. To bypass this I used reflection and made this method:
public StaticClass toNashornClass(Class<?> c) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{
Class<?> cl = Class.forName("jdk.internal.dynalink.beans.StaticClass");
Constructor<?> constructor = cl.getDeclaredConstructor(Class.class);
constructor.setAccessible(true);
StaticClass o = (StaticClass) constructor.newInstance(c);
return o;
}
If I pass the Class of the object I want as a global variable I just need to call toNashornClass(Example.class); and put the resulting object into a global var with engine.put("example",object);
It works fine. I can use the example var completely like a var created by Java.type().
I am trying to implement a simple callback from Delphi to Java using JNA using the following java code:
package jnaapp;
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import com.sun.jna.Callback;
public class JnaAppTest {
public interface CLibrary extends Library {
CLibrary INSTANCE = (CLibrary)
Native.loadLibrary((Platform.isWindows() ? "helloDelphi" : "c"),
CLibrary.class);
public interface eventCallback extends Callback {
public void callback(int id);
}
boolean setCallback(eventCallback callback);
boolean TestFunction(byte[] text, int length);
}
public static void main(String[] args) throws Exception{
byte[] text = new byte[100];
CLibrary.eventCallback callback = new CLibrary.eventCallback(){
public void callback(int id){
System.out.println("I was called with: "+id);
}
};
System.out.println(CLibrary.INSTANCE.setCallback(callback));
System.out.println(CLibrary.INSTANCE.TestFunction(text, 100));
System.out.println(Native.toString(text));
}
}
The corresponding Delphi code is given below:
Library helloDelphi;
uses
SysUtils,
Classes;
{$R *.res}
type TCallback = procedure(val: Integer); stdcall;
var
cb : TCallback;
function setCallback(callBack : TCallback) : WordBool; stdcall; export;
begin
cb := callBack;
Result := True;
end;
function TestFunction(stringBuffer : PAnsiChar; bufferSize : integer) : WordBool; stdcall; export
var s : string;
begin
s := 'Hello World 2';
StrLCopy(stringBuffer, PAnsiChar(s), bufferSize-1);
cb(bufferSize);
Result := True;
end;
exports TestFunction;
exports setCallback;
begin
end.
When the callback is called from Delphi, this crashes the VM. If I remove the callback from Testfunction, everything works fine! Thanks for your help!
Delphi uses the stdcall calling convention, so you need to use StdCallLibrary, not Library. The wrong convention will cause a crash because the called function will be expecting to use a different stack layout than the calling code.
You'll also need to use StdCallCallback rather than Callback.
I am new to accessing DLLs from Java using JNA. I need to access methods from a class within a DLL(written in .net). Form this sample DLL below, I am trying to get AuditID and Server ID. I am ending with the following error while I am running my code. Any guidance really appreciated.
/// Error ///
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'GetEnrollcontext': The specified procedure could not be found.
//DLL File Code//
SampleDLL.ProfileEnroll enrollcontext = new SampleDLL.ProfileEnroll();
enrollcontext.Url =” url”;
enrollcontext.AuditIdType = SampleDLL.ProfileId;
enrollcontext.AuditId = “22222222 “;
enrollcontext.ServerId = “server1”;
/// Java Code ///
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Structure;
import dllExtract.DLLExtractTest.SampleDLL.Enrollcontext;
public class SampleDLLExtract {
public interface SampleDLL extends Library {
SampleDLL INSTANCE = (SampleDLL) Native.loadLibrary("SampleDLL",
SampleDLL.class);
public static class Enrollcontext extends Structure {
public String auditId;
public String serverId;
}
void GetEnrollcontext(Enrollcontext ec); // void ();
}
public static void main(String[] args) {
SampleDLL sdll = SampleDLL.INSTANCE;
SampleDLL.Enrollcontext enrollContext = new SampleDLL.Enrollcontext();
sdll.GetEnrollcontext(enrollContext);
System.out.println(sdll.toString(sdll.GetEnrollcontext(enrollContext)));
}
}
in fact there is a solution for you to use C#, VB.NET or F# code via JNA in Java (and nothing else)! and it is also very easy to use:
https://www.nuget.org/packages/UnmanagedExports
with this package all you need to do is, add [RGiesecke.DllExport.DllExport] to your methods like that:
C# .dll Project:
[RGiesecke.DllExport.DllExport]
public static String yourFunction(String yourParameter)
{
return "CSharp String";
}
Java Project:
public interface jna extends Library {
jna INSTANCE = (jna) Native.loadLibrary("yourCSharpProject.dll", jna.class);
public String yourFunction(String yourParameter);
}
use it in the code:
System.out.println(jna.INSTANCE.yourFunction("nothingImportant"));
Viola!
As already mentioned it works very easy, but this solution has some limitations:
only available for simple datatypes as parameter & return values
no MethodOverloading available. yourFunction(String yourParameter) and yourFunction(String yourParameter, String yourSecondParameter) does not work! you have to name them differently
Use arrays as parameter or return values. (JNA offers StringArray, but I am not able to use them in C#) (maybe there is a solution, but I couldn't come up with one so far!)
if you export a method you can't call it internally in your C# code (simple to bypass that by the following:
.
[RGiesecke.DllExport.DllExport]
public static Boolean externalAvailable(String yourParameter)
{
return yourInternalFunction(yourParameter);
}
With C# it works great, with VB.NET and F# I have no experience.
hope this helps!