JRuby class and Java Scripting Engine unexpected results - java

I'm experimenting with the Java scripting engine and Ruby, and I'm having trouble setting some instance variables in a ruby script. This could be my lack of understanding of Ruby or my lack of understanding of how to use ruby classes in the scripting engine. With the following code:
public class App {
public static void main( String[] args ) throws Exception{
ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine se = sm.getEngineByName("jruby");
StringBuilder sb = new StringBuilder();
sb.append("class Test\n");
sb.append(" attr_accessor :a, :b\n");
sb.append(" def str\n");
sb.append(" \"#{a}, #{b} is a test.\"\n");
sb.append(" end\n");
sb.append("end\n");
sb.append("o = Test.new\n");
Object o = se.eval(sb.toString());
se.put("#a", "A");
se.put("#b", "B");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
}
}
I'd expect the output to be 'A, B is a test'
Instead, the output is ', is a test'.
How should I be setting variables a, b in this code?
Edit: Just to be clear, ideally I don't want to be setting the variables by appending them to this StringBuilder - this is just for illustration. In practice, I'll be loading scripts from some source, and then want to set properties and call methods on that Ruby object afterwards. I'm sure I'm just missing some crucial step that everyone else knows about :). Thanks to Gareth Davis' answer I've found I can use bindings and global variables successfully, but that isn't going to work with all scripts. Would really appreciate any links to good articles that go beyond 'hello world' type usage, as I've not found any decent ones.
Second edit: This is the working, final code, with the crucial line that I knew must be missing :)-
public class App {
public static void main( String[] args ) throws Exception{
//Must set this property if you want to call eval multiple times!
System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
ScriptEngineManager sm = new ScriptEngineManager();
ScriptEngine se = sm.getEngineByName("jruby");
StringBuilder sb = new StringBuilder();
sb.append("class Test\n");
sb.append(" attr_accessor :a, :b\n");
sb.append(" def str\n");
sb.append(" \"#{a}, #{b} is a test.\"\n");
sb.append(" end\n");
sb.append("end\n");
sb.append("o = Test.new\n");
Object o = se.eval(sb.toString());
se.eval("o.a = \"A\"");
se.eval("o.b = \"B\"");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));
}
}

That won't work like that. The only way to set the values of a & b is to evaluate o.a = 'A' and o.b = 'B'.
The first solution is to amend the script to populate the values thus:
sb.append("o.a = 'A'\n");
sb.append("o.b = 'B'\n");
sb.append("o");
Object o = se.eval(sb.toString());
I've created a working example on github.com
keeping with the question the following can be used (credit to #Mick Sear):
System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
// .. snip
Object o = se.eval(sb.toString());
se.eval("o.a = 'A'");
se.eval("o.b = 'B'");
System.out.println( ((Invocable) se).invokeMethod(o, "str"));

Related

Why wont this .jar file run when I try to start it on C#? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I'm very new to the coding space and was wondering if someone could help me start a .jar file. BTW This is using C#. My issue is this wont run the file. I got it to work with .txt files though, so I'm just a bit confused.
private void button1_Click(object sender, EventArgs e)
{
Process.Start("java" , "server.jar");
}
In short, for the answer, add -jar right before the JAR file name.
The accepted answer is not 100% correct for several reasons: it does not recognize whitespace-delimited and whitespace-containing arguments, and may mess up with quote characters that must be passed (therefore properly escaped) to the delegated Java app. In short, do not use Arguments if the string is not known to be a constant (having spaces will require manual escaping anyway), but merely prefer ArgumentList that handles each argument properly.
Here is an example Java application to deal with command line arguments:
public final class SayHello {
private SayHello() {}
public static void main(final String... names) {
for ( final String name : names ) {
System.out.printf("hello %s!\n", name);
}
}
}
The manifest for the JAR file:
Manifest-Version: 1.0
Main-Class: SayHello
Making a JAR file out of it is simple:
javac SayHello.java
jar cfm SayHello.jar MANIFEST.MF SayHello.class
Example of use:
java -jar SayHello.jar 'John Doe' Anonymous
that gives:
hello John Doe!
hello Anonymous!
Now, an example C# program that passes the -jar argument to the java process so that it recognizes the given file as a JAR file and demonstrates what can go wrong with Arguments if passed as a string.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
</PropertyGroup>
</Project>
using System.Diagnostics;
public static class SayHello {
public static void Main() {
// interprets 3 names: John, Doe, Anonymous (wrong)
RunJavaJarBadly1("SayHello.jar", "John Doe Anonymous");
// interprets 1 name: John Doe Anonymous (wrong)
RunJavaJarBadly2("SayHello.jar", "John Doe Anonymous");
// interprets 2 names: John Doe, Anonymous (correct, but bad: requires the first name to be quoted at the call-site)
RunJavaJarBadly1("SayHello.jar", "\"John Doe\" Anonymous");
// interprets 1 name: "John Doe" Anonymous (wrong: interprets everything as a single name)
RunJavaJarBadly2("SayHello.jar", "\"John Doe\" Anonymous");
// interprets 2 names, no ambiguous call, each name is recognized properly, does not require quoting at the call site
RunJavaJar("SayHello.jar", "John Doe", "Anonymous");
}
private static void RunJavaJarBadly1(string jarPath, string argumentsFortheJarFile) {
var process = new Process();
process.StartInfo.FileName = "java";
process.StartInfo.Arguments = #"-jar "+ jarPath +" " + argumentsFortheJarFile;
process.Start();
process.WaitForExit();
}
private static void RunJavaJarBadly2(string jarPath, string jarArgs) {
var process = new Process();
process.StartInfo = new ProcessStartInfo("java") {
ArgumentList = { "-jar", jarPath, jarArgs }
};
process.Start();
process.WaitForExit();
}
private static void RunJavaJar(string jarPath, params string[] jarArgs) {
var process = new Process();
process.StartInfo = new ProcessStartInfo("java") {
ArgumentList = { "-jar", jarPath }
};
foreach ( var jarArg in jarArgs ) {
process.StartInfo.ArgumentList.Add(jarArg);
}
process.Start();
process.WaitForExit();
}
}
The code above produces (no legend in the output, but added for explanation):
hello John! \_ #1/1: incorrect, the space is ignored
hello Doe! /
hello Anonymous! -- #1/2: correct, no spaces in-between
hello John Doe Anonymous! -- #2/1|2: incorrect
hello John Doe! -- #3/1: correct, but requires the call site to escape the argument
hello Anonymous! -- #3/2: correct, no need to escape, thanks to no spaces
hello "John Doe" Anonymous! -- #4/1|2: incorrect, similar to #2/1|2
hello John Doe! -- #5/1: correct, let the framework do its job
hello Anonymous! -- #5/2: correct, let the framework do its job
In order to get it to work, the file name needs to be "java" and contain the file location in the arguments.
System.Diagnostics.Process clientProcess = new Process();
clientProcess.StartInfo.FileName = "java";
clientProcess.StartInfo.Arguments = #"-jar "+ jarPath +" " + argumentsFortheJarFile;
clientProcess.Start();
clientProcess.WaitForExit();
int code = clientProcess.ExitCode;
Taken from similar question here
Optional way using ArgumentList:
System.Diagnostics.Process clientProcess = new Process();
var info = new System.Diagnostics.ProcessStartInfo("java.exe")
{
ArgumentList = {
"-jar",
jarPath,
jarArgs
}
};
info.FileName = "java";
clientProcess.StartInfo = info;
clientProcess.Start();
clientProcess.WaitForExit();
int code = clientProcess.ExitCode;
Here are some options for you to check out.
Also similar question with a working result: here
Paraphrasing from links:
In order to get it to work, the file name needs to be "java" and contain the file location in the arguments.
System.Diagnostics.Process clientProcess = new Process();
clientProcess.StartInfo.FileName = "java";
clientProcess.StartInfo.Arguments = #"-jar "+ jarPath +" " + argumentsFortheJarFile;
clientProcess.Start();
clientProcess.WaitForExit();
int code = clientProcess.ExitCode;

How to get an Initial Contex from Wildfly 8

ADDED 7/23.
Many views: Not even a "that's dumb" question in response. Can anyone at least tell me why such an embarrassingly trivial question seems to have no answer anywhere.
Q:
--- Have Wildfly 8 running on local machine localhost:9990.
--- Have a Java program that need's Wildfly's IntialContext.
--- Every reference says use: "Context ctx = new InitialContext(env);"
--- Yet a week of searching turns up no set of properties that returns one.
And no example of a java program that gets one.
Does no one ever do this? Really need help
Original Msg Below
I know many people have asked how to get an Initial context from Wildfly 8. But I have yet to find a simple answer with a simple example.
Therefore, I hope someone can tell my why this doesn’t work.
I start Wildfly with standalone-full.xml
The three sections below have
A - Code summary of my test Class whose only purpose is to secure an Initial Context. (I only removed a lot of printing code that produced the next section.]
B - The Eclipse console output for a failure.
C - Cut and paste code. Just in case anyone can help me get this to work. I’d like to leave behind something the next new WF user can cut and past and run. The only difference from 1 above is that this version has all the static methods I used to format the output. NOTE: I know the comments I inserted about the less than sign sound dumb. BUT ... they are true.
A Code Summary
import java.util.Properties;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.InitialContext;
public class JmsTestGetJNDIContext {
//members
final private Properties env = new Properties() {
private static final long serialVersionUID = 1L;
{
/* These are Properties used by a standalone JavaClient to secure a WIldFly InitialContext()*/
put(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
put(Context.PROVIDER_URL,"http-remoting://localhost:9990");
put(Context.SECURITY_PRINCIPAL,"userGLB");
put(Context.SECURITY_CREDENTIALS,"Open");
put("jboss.naming.client.ejb.context", true);
/*The above URL, ID and PW successfully open Wildfly's Admin Console*/
}
};
//constructor
private JmsTestGetJNDIContext (){
/*print "beg"*/
/*print "env"*/
try {
/*print "Requesting InitialContext"*/
Context ctx = new InitialContext(this.env);
/*print "JNDI Context: " + ctx)*/
/*print "end");
} catch (CommunicationException e) {
/* print "You forgot to start WildFly dummy!"*/
} catch (Exception e) {
/* print"caught: " + e.getClass().getName()*/
/*print e.getMessage()*/
/* "end")*/
}
static public void main (String[] args) {
/*print "beg"*/
JmsTestGetJNDIContext client = new JmsTestGetJNDIContext ();
/*print "end"*/
}
}
B - Console Output
JmsTestGetJNDIContext.main () beg
JmsTestGetJNDIContext.<init> () beg
JmsTestGetJNDIContext.<init> () These are Properties used to obtain IntialContext
Key: java.naming.provider.url
Value: http-remoting://localhost:9990
Key: java.naming.factory.initial
Value: org.jboss.naming.remote.client.InitialContextFactory
Key: jboss.naming.client.ejb.context
Value: true
Key: java.naming.security.principal
Value: userGLB
Key: java.naming.security.credentials
Value: Open
JmsTestGetJNDIContext.<init> () Requesting InitialContext
JmsTestGetJNDIContext.<init> () caught: javax.naming.NamingException
JmsTestGetJNDIContext.<init> () Failed to create remoting connection
JmsTestGetJNDIContext.<init> () end
JmsTestGetJNDIContext.main () end
Cut and Paste Code
package org.america3.gotest.xtra;
import java.util.Properties;
import javax.naming.CommunicationException;
import javax.naming.Context;
import javax.naming.InitialContext;
public class JmsTestGetJNDIContext {
//members
final private Properties env = new Properties() {
/**
* Properties used by a standalone JavaClient to secure
* a WIldFly InitialContext()*/
private static final long serialVersionUID = 1L;
{
put(Context.INITIAL_CONTEXT_FACTORY,"org.jboss.naming.remote.client.InitialContextFactory");
put(Context.PROVIDER_URL, "http-remoting://localhost:9990");
put(Context.SECURITY_PRINCIPAL, "userGLB");
put(Context.SECURITY_CREDENTIALS, "Open");
// The above URL, ID and PW successfully open Wildfly's Admin Console
put("jboss.naming.client.ejb.context", true);
}
};
//constructor
private JmsTestGetJNDIContext (){/*ignore*/String iAm = JmsTestGetJNDIContext.getIAm(" ", Thread.currentThread().getStackTrace());
P (iAm, "beg");
pProps(iAm, env);
try {
P (sp + iAm, "Requesting InitialContext");
Context ctx = new InitialContext(this.env);
P (sp + iAm, "JNDI Context: " + ctx);
P (sp + iAm, "end");
} catch (CommunicationException e) {
P (sp + iAm, "You forgot to start WildFly dummy!");
} catch (Exception e) {
P (sp + iAm, "caught: " + e.getClass().getName());
P (sp + iAm, e.getMessage());
P (iAm, "end");
}
}
static public void main (String[] args) {/*ignore*/String iAm = JmsTestGetJNDIContext.getIAm("",Thread.currentThread().getStackTrace());
P (iAm, "beg");
JmsTestGetJNDIContext client = new JmsTestGetJNDIContext ();
P (iAm , "end");
}
/*The remaining static methods are just to facilitate printing.
* They are normally in a Untility package I add to my projects.
* I put them here so this code would run for anyone.*/
static private void pProps (String leader, Properties p) {
StringBuffer sb = new StringBuffer ();
String s = JmsTestGetJNDIContext.padRight(leader, 45, ' ');
s = " " + s + "These are Properties used to obtain IntialContext"+"\n";
sb.append(s);
String skip = "";
for (Object key: p.keySet()) {
sb.append(skip + " " + JmsTestGetJNDIContext.padRight("\""
+ (String)key + "\"", 40, ' ')
+ " \"" + p.get(key) + "\"");
skip = "\n";
}
System.out.println(sb);
}
static private void P (String s, String s2) {
System.out.println(s + s2);
}
static public String getClassMethodName (StackTraceElement[] elements) {
String className = null;
for (int i = 0; i * elements.length; i++]i ) {
/* You need to type in a less than sign for the '*'
* because when I do, the editor will not show any code
* that comes after it.
* I have no idea why, but I've spent over an hour trying,
* and every time I type a less than sign all the following
* code dissappears!*/
className = elements[i].getClassName ();
if (className.startsWith ("org.america3")) {
int end = className.lastIndexOf ('.');
return className.substring (end + 1) + "." + elements[i].getMethodName ();
} else {
continue;
}
}
return "no project method found in elements beginning with org.america3" ;
}
static private String getIAm (String indent, StackTraceElement[] elements) {
StringBuffer sb = new StringBuffer ();
sb.append(JmsTestGetJNDIContext.getClassMethodName(elements));
sb.append(" ()");
return indent + JmsTestGetJNDIContext.padRight (sb.toString(), 45, ' ') ;
}
static public String padRight(String s, int width, char c){
if (s == null) return "Null String";
if(s.length() ** width){
/* You need to type in a greater than or equal sign for
* the '**'see above.*/
return s;
} else {
StringBuffer sb = new StringBuffer();
sb.append (s);
for(int i = 0; i *** (width - s.length()); i++){
/*You need to type in a less than sign the '***'. Again see above*/
sb.append(c);
}
return sb.toString();
}
}
static public String sp = " ";
}
A while ago I also struggled with remote EJBs in my CLI application. I excavated a small example project that I wrote then. It gets an InitialContext and calls a remote EJB named AddBrackets:
import java.util.Properties;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import de.dnb.test.ejb.AddBrackets;
public final class Application {
public static void main(String[] args) throws NamingException {
final Properties jndiProperties = initJndiProperties();
final AddBrackets addBrackets = getEjb(jndiProperties);
System.out.println(addBrackets.processText("Hello World"));
}
private static Properties initJndiProperties() {
final Properties jndiProperties = new Properties();
jndiProperties.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jboss.naming.remote.client.InitialContextFactory");
jndiProperties.put("jboss.naming.client.ejb.context", true);
jndiProperties.put(Context.PROVIDER_URL, "http-remoting://localhost:8080/");
//jndiProperties.put(Context.SECURITY_PRINCIPAL, "test");
//jndiProperties.put(Context.SECURITY_CREDENTIALS, "test");
return jndiProperties;
}
private static AddBrackets getEjb(Properties jndiProps)
throws NamingException {
final Context jndiContext = new InitialContext(jndiProps);
final String interfaceName = AddBrackets.class.getName();
return (AddBrackets) jndiContext.lookup(
"ejbtest-app-1.0-SNAPSHOT/ejbtest-ejb-1.0-SNAPSHOT/AddBracketsBean!"
+ interfaceName);
}
}
I built this program as a Maven project which had a dependency on
<dependency>
<groupId>org.wildfly</groupId>
<artifactId>wildfly-ejb-client-bom</artifactId>
<version>8.2.1.Final</version>
<type>pom</type>
</dependency>
This dependency brings in Wildfly's remote client EJB implementation and adds the following jars to the class path (links are to Maven Central):
jboss-logging-3.1.4.GA.jar
jboss-marshalling-1.4.9.Final.jar
jboss-marshalling-river-1.4.9.Final.jar
jboss-remoting-4.0.7.Final.jar
jboss-sasl-1.0.4.Final.jar
jboss-ejb-api_3.2_spec-1.0.0.Final.jar
jboss-transaction-api_1.2_spec-1.0.0.Final.jar
xnio-api-3.3.0.Final.jar
xnio-nio-3.3.0.Final.jar
jboss-ejb-client-2.0.1.Final.jar
jboss-remote-naming-2.0.1.Final.jar
wildfly-build-config-8.2.1.Final.jar
I did no special configuration on Wildfly to run this example. I simply downloaded a vanilla Wildfly 8.2.1, unzipped it, set up an admin user with the add-user.sh script and deployed my EJB in an EAR. As you can see above access is granted without a username and a password.
You can find the complete project including the AddBrackets EJB on my bitbucket account.
When I tried to get my head around remote EJBs with Wildfly, I found the article JBoss EAP / Wildfly – Three ways to invoke remote EJBs really helpful. It clearly describes the three different methods to access remote EJBs on Wildfly.
According to your own answer the following jars are on your classpath:
jboss-remote-naming-1.0.7.final.jar
jboss-logging.jar
xnio-api-3.0.7.ga.jar
jboss-remoting-3.jar
jboss-ejb-client-1.0.19.final.jar
You write that the application throws the following exception:
java.lang.NoSuchMethodError:
org.jboss.remoting3.Remoting.createEndpoint(Ljava/lang/String;Lorg/xnio/OptionMap;)Lorg/jboss/remoting3/Endpoint;]
This exception is thrown when org.jboss.naming.remote.client.EndpointCache which is part of the jboss-remote-naming jar tries to call Remoting.createEndpoint() which is contained in the jboss-remoting jar.
As you explain in your answer the reason for this is that the Remoting class declares a 3-parameter version of the createEndpoint() method while the EndpointCache class tries to call a 2-parameter version which does not exist.
I checked the commit histories and declared dependencies of the jboss-remote-naming and the jboss-remoting projects to find out what is going wrong there. This is what I found out:
The 2-parameter version of createEndpoint() was only added in version 3.2 of jboss-remoting. The pom.xml for jboss-remote-naming-1.0.7.final says it depends on jboss-remoting 3.2.7.GA.
As there is no version number on your jboss-remoting-3.jar, I guess it is an older version. You should be able to check this by looking for a pom.xml in META-INF folder of your jboss-remoting-3.jar. This should contain the version number.
To solve your problem, I suggest to replace your jboss-remoting-3.jar with jboss-remoting-3.2.7ga.jar or to use the set of jars I listed in my other answer.
I’ve decided the problem isn’t coding or the JNDI InititialContext Properties.
I mean the fatal error is a NoSuchMethodError. Therefore, as I confirmed in the WildFly server logs, my main method never even tries to connect.
Here’s what I think explains the real problem.
And I think it explains why there are so many calls for help with this error:
java.lang.NoSuchMethodError:
org.jboss.remoting3.Remoting.createEndpoint(Ljava/lang/String;Lorg/xnio/OptionMap;)Lorg/jboss/remoting3/Endpoint;]
Also why none of those calls for help ever get a conclusive answer. Just people suggesting different jars.
And since all those answers fixed on jars, this is how I tested the Build Path I was using:
First I removed all jars from the Build Path. Then I ran my one line main program till all ClassNotFoundException were gone.
First Error
java.lang.ClassNotFoundException:
org.jboss.naming.remote.client.InitialContextFactory]
Added jboss-remote-naming-1.0.7.final.jar to class path
Next Error
java.lang.NoClassDefFoundError:
org/jboss/logging/Logger
Added jboss-logging.jar
Next Error
java.lang.NoClassDefFoundError:
org/xnio/Options
Added xnio-api-3.0.7.ga.jar
Next Error
java.lang.NoClassDefFoundError:
org/jboss/remoting3/spi/ConnectionProviderFactory
Added jboss-remoting-3.jar
Next Error
java.lang.NoClassDefFoundError:
org/jboss/ejb/client/EJBClientContextIdentifier
Added jboss-ejb-client-1.0.19.final.jar
FATAL ERROR (note: All NoClassDefFoundError have been cleared)
java.lang.NoSuchMethodError:
org.jboss.remoting3.Remoting.createEndpoint(Ljava/lang/String;Lorg/xnio/OptionMap;)Lorg/jboss/remoting3/Endpoint;]
Then I used Eclipse’s Project Explorer to verify:
That jboss-remoting3.jar has the org.jboss.remoting3.Remoting Class. It does. That’s why there is no NoClassDefFoundError left above.
And verified it had this method:
public Endpoint createEndpoint (String, Executor, OptionMap) note: 3 parameters.
BUT the above Error indicates something is calling:
public Endpoint createEndpoint (String, OptionMap) note: 2 parameters.
That’s why the program throws a NoSuchMethodError. It is looking for a 2 paramater version of org.jboss.remoting3.Remoting.createEndpoint(). And the Remoting Class I have only has a 3 parameter version.`
I know this sounds impossible but the only thing I can think is there is an inconsistency in the Java API???
Clearly something is calling org.jboss.remoting3.Remoting.createEndpoint with 2 parameters.
But my org.jboss.remoting3.Remoting Class only has a 3 parameter version of the createEndpoint() Method.
So I’m going to clean this all up and repost a question asking how to explain the existence of a Class calling for a 2 paramter org.jboss.remoting3.Remoting.createEndpoint Method when I have a jar whose org.jboss.remoting3.Remoting only offers a 3-parameter.
Here is your obligatory "that's a dumb question." Does the wildfly remote quickstart github repo answer the question for you? Their code, from RemoteEJB.java
final Hashtable<String, String> jndiProperties = new Hashtable<>();
jndiProperties.put(Context.URL_PKG_PREFIXES, "org.jboss.ejb.client.naming");
final Context context = new InitialContext(jndiProperties);
return (RemoteCalculator) context.lookup("ejb:/ejb-remote-server-side/CalculatorBean!" + RemoteCalculator.class.getName());

LUAJ Coerced Java object won't accept LuaValue argument

I've come across an issue with LuaJ not accepting an LuaValue as an argument when the Java code specifically asks for an LuaValue.
public void registerEvent(LuaValue id, String event, String priority,
LuaValue callback)
{
if(!(id instanceof LuaTable))
{
throw new RuntimeException("id must be an LuaTable");
}
EventDispatcher.addHandler(id, event, priority, callback);
}
Ideally, this would allow the code in Lua to simply read like so...
function main(this)
this.modName="Some Mod"
this.lastX = 0
hg.both.registerEvent(this, "inputcapturedevent", "last", eventRun)
end
function eventRun(this, event)
this.lastX += event.getX()
end
Sadly, this simple gives an error that it expects userdata, but got a table.
org.luaj.vm2.LuaError: script:4 bad argument: userdata expected, got table
The value of "this" is the same LuaTable in both cases, but because the method registerEvent is added via CoerceJavaToLua.coerce(...) it believes it wants a java Object instead of realising it really wants an LuaVale.
So my question is this. Is there a better way around this that allows me to use the same function from both Java and Lua? And thanks for your time if you read it all the way here :)
The error you are getting is probably a red herring and may be due to the way you are binding the "registerEvent" function into the value of "hg.both". Possibly you just need to use the method syntax instead, such as
hg.both:registerEvent(this, "inputcapturedevent", "last", eventRun)
If you want to use the dot syntax hg.both.registerEvent, then using VarArgFunction and implementing invoke() may be a more direct way to implement this. In this example, Both.registerEvent is a plain variable that is a VarArgFunction.
public static class Both {
public static VarArgFunction registerEvent = new VarArgFunction() {
public Varargs invoke(Varargs args) {
LuaTable id = args.checktable(1);
String event = args.tojstring(2);
String priority = args.tojstring(3);
LuaValue callback = args.arg(4);
EventDispatcher.addHandler(id, event, priority, callback);
return NIL;
}
};
}
public static void main(String[] args) throws ScriptException {
ScriptEngineManager sem = new ScriptEngineManager();
ScriptEngine engine = sem.getEngineByName("luaj");
Bindings sb = engine.createBindings();
String fr =
"function main(this);" +
" this.modName='Some Mod';" +
" this.lastX = 0;" +
" hg.both.registerEvent(this, 'inputcapturedevent', 'last', eventRun);" +
"end;";
System.out.println(fr);
CompiledScript script = ((Compilable) engine).compile(fr);
script.eval(sb);
LuaFunction mainFunc = (LuaFunction) sb.get("main");
LuaTable hg = new LuaTable();
hg.set("both", CoerceJavaToLua.coerce(Both.class));
sb.put("hg", hg);
LuaTable library = new LuaTable();
mainFunc.call(CoerceJavaToLua.coerce(library));
}

Static Java function imported with rJava doesn't work with tm_map()

I've prepared a class with a static method in Java 6, which I've exported to a JAR file:
package pl.poznan.put.stemutil;
public class Stemmer {
public static String stemText(String text) {
Set<String> c = new HashSet<String>();
...
return StringUtils.join(c, " ");
}
}
I import it to R with following code:
require(rJava)
.jinit("java/stem-util.jar")
stem = J("pl.poznan.put.stemutil.Stemmer")$stemText
Then, when I call it directly it works, e.g:
> stem("płotkami")
[1] "płotek płotka"
But when I'll try to use it with tm_map() function, something goes wrong:
> vc = VCorpus(vs, readerControl = list(language = "pl"))
> vc[[1]]
<<PlainTextDocument (metadata: 7)>>
mirki mirkówny zaczynam wolne jutra ( ͡° ͜ʖ ͡°) #pijzwykopem #piwozlidla
> vc = tm_map(vc, stem)
Komunikat ostrzegawczy:
In mclapply(content(x), FUN, ...) :
all scheduled cores encountered errors in user code
> vc[[1]]
[1] "Error in FUN(X[[1L]], ...) : \n Sorry, parameter type `NA' is ambiguous or not supported.\n"
attr(,"class")
[1] "try-error"
attr(,"condition")
<simpleError in FUN(X[[1L]], ...): Sorry, parameter type `NA' is ambiguous or not supported.>
What am I doing incorrectly?
Finally adding mc.cores parameter has worked for me. However, It's more a workaround, than a proper solution.
vc = tm_map(vc, content_transformer(stem), mc.cores=1)

JPype Passing args to Java

I have a java facade class I'm trying to access from python so I decided to use JPype. My facade class only has one constructor (no default) with four args
public facade(String a, String b, List<String> c, List<String> d){
...
}
I can't seem to get the types correct when initializing a new instance of the class. Everything I try gives the same error:
File ".../main.py", line 34, in __init__
facadeinstance = Facade(jpype.JString(s1), jpype.JString(s2),jpype.JArray(jpype.java.lang.String, 1)(s3), jpype.JArray(jpype.java.lang.String, 1)(s4))
File "/usr/local/lib/python2.7/dist-packages/jpype/_jclass.py", line 79, in _javaInit
self.__javaobject__ = self.__class__.__javaclass__.newClassInstance(*args)
RuntimeError: No matching overloads found. at src/native/common/jp_method.cpp:121
I know JPype is working. I've tried several combinations of wrappers to get the data in the right form with no luck.
Relevant code:
import jpype
s1 = "something"
s2 = "something else"
s3 = ["something in a list"]
s4 = ["Something else in a list"]
jpype.startJVM(jpype.getDefaultJVMPath(), "-Djava.class.path=" + JavaJarDir)
myLib = jpype.JPackage('myLib')
Facade = myLib.Facade # class loads fine, resources printed to stdout
# The error occurs on the next line
FacadeInstance = Facade(jpype.JString(s1), jpype.JString(s2), jpype.JArray(jpype.java.lang.String, 1)(s3), jpype.JArray(jpype.java.lang.String, 1)(s4))
jpype.shutdownJVM()
JArray(JString) won't match List. You have to use jpype.java.util.ArrayList() (or anything that implements List).
myArray = ["A", "B", "C"]
myList = jpype.java.util.ArrayList()
for s in myArray:
myList.add(s)
So your code will look like that:
import jpype
s1 = "something"
s2 = "something else"
s3 = ["something in a list"]
s4 = ["Something else in a list"]
jpype.startJVM(jpype.getDefaultJVMPath(), "-Djava.class.path=" + JavaJarDir)
# Import Java library and class
myLib = jpype.JPackage('myLib')
Facade = myLib.Facade
# Prepare List<String> arguments
arg3 = jpype.java.util.ArrayList()
for s in s3:
list3.add(s)
arg4 = jpype.java.util.ArrayList()
for s in s4:
list4.add(s)
FacadeInstance = Facade(jpype.JString(s1), jpype.JString(s2), arg3, arg4)
jpype.shutdownJVM()

Categories