I'm trying to figure out how to add constructor parameters to my JRuby Script. I have had it working before with the following code.
class Man < NpcCombat
def attackScripts attacker, victim
return [BasicAttack.meleeAttack(attacker, victim,AttackStyle::Mode::MELEE_ACCURATE, 2, Weapon::FISTS)]
end
end
However the Java Class "NpcCombat" now has a integer parameter, such as NpcCombat(int). I'm trying to figure out how to change this in my ruby script, but it's not working.
I've never used jruby, but based on Ruby I imagine adding an initialize block that calls the super constructor should work:
class Man < NpcCombat
def initialize(num)
super(num)
end
...
end
Related
I have implemented a method that generates Java code:
public static String generate() { ... }
It returns Java code as a String, i.e. I might get
"public class X { public static String x() { return \"x\"; } }"
as a value returned by generate. Now, I am in a JShell Tool (JEP-222) session, and I can call generate, but I fail to load the result of generate into JShell itself.
Ideally I'd like to achieve the following
jshell> eval(generate());
| created class X
jshell> X.x();
$2 ==> "x"
In the above (hypothetical) JShell sesssion, eval is the function that I am looking for.
Possible solutions I already tried:
I know that this would be possible by calling JShell#eval but I failed to obtain the JShell object that represents the currently running JShell.
It might be that there is some facility in JShell that allows lifting a String to a Snippet. I couldn't find something like that, but if there is, it might be helpful.
I know that I can /open a file, so it might be possible to write the String to a file and then open/load it. However this is a two-step process, and I would prefer to have a solution that is simpler.
From this answer, I learnt that, every Groovy script compiles to a class that extends groovy.lang.Script class
Below is a test groovy script written for Jenkins pipeline in Jenkins editor.
node('worker_node'){
print "***1. DRY principle***"
def list1 = [1,2,3,4]
def list2 = [10,20,30,40]
def factor = 2
def applyFactor = {e -> e * factor}
print(list1.each(applyFactor))
print(list2.each(applyFactor))
print "***2. Higher order function***"
def foo = { value, f -> f(value *2) }
foo(3, {print "Value is $it"})
foo(3){
print "Value is $it"
}
}
How to compile this groovy script to see the class generated(source code)?
The class generated is bytecode, not source code. The source code is the Groovy script.
If you want to see something similar to what the equivalent Java source code would look like, use groovyc to compile the script as usual, and then use a Java decompiler to produce Java source (this question's answers lists a few).
That's subject to the usual caveats on decompiled code, of course. High-level information is lost in the process of compiling. Decompilers have to guess a bit to figure out the best way to represent what might have been in the original source. For instance, what was a for loop in the original code may end up being decompiled as a while loop instead.
groovy in jenkins pipeline is a Domain Specific Language.
It's not a plain groovy.
However if you remove node(){ } then it seems to be groovy in your case.
and you can run it in groovyconsole or compile to class with groovyc
just download a stable groovy binary and extract it.
if you have java7 or java8 on your computer - you can run groovyconsole and try your code there.
with Ctrl+T you can see the actual class code generated for your script.
I wrote the following MyPythonGateway.java so that I can call my custom java class from Python:
public class MyPythonGateway {
public String findMyNum(String input) {
return MyUtiltity.parse(input).getMyNum();
}
public static void main(String[] args) {
GatewayServer server = new GatewayServer(new MyPythonGateway());
server.start();
}
}
and here is how I used it in my Python code:
def main():
gateway = JavaGateway() # connect to the JVM
myObj = gateway.entry_point.findMyNum("1234 GOOD DAY")
print(myObj)
if __name__ == '__main__':
main()
Now I want to use MyPythonGateway.findMyNum() function from PySpark, not just a standalone python script. I did the following:
myNum = sparkcontext._jvm.myPackage.MyPythonGateway.findMyNum("1234 GOOD DAY")
print(myNum)
However, I got the following error:
... line 43, in main:
myNum = sparkcontext._jvm.myPackage.MyPythonGateway.findMyNum("1234 GOOD DAY")
File "/home/edamameQ/spark-1.5.2/python/lib/py4j-0.8.2.1-src.zip/py4j/java_gateway.py", line 726, in __getattr__
py4j.protocol.Py4JError: Trying to call a package.
So what did I miss here? I don't know if I should run a separate JavaApplication of MyPythonGateway to start a gateway server when using pyspark. Please advice. Thanks!
Below is exactly what I need:
input.map(f)
def f(row):
// call MyUtility.java
// x = MyUtility.parse(row).getMyNum()
// return x
What would be the best way to approach this? Thanks!
First of all the error you see usually means the class you're trying to use is not accessible. So most likely it is a CLASSPATH issue.
Regarding general idea there are two important issues:
you cannot access SparkContext inside an action or transformation so using PySpark gateway won't work (see How to use Java/Scala function from an action or a transformation? for some details)). If you want to use Py4J from the workers you'll have to start a separate gateways on each worker machine.
you really don't want to pass data between Python an JVM this way. Py4J is not designed for data intensive tasks.
In PySpark before start calling the method -
myNum = sparkcontext._jvm.myPackage.MyPythonGateway.findMyNum("1234 GOOD DAY")
you have to import MyPythonGateway java class as follows
java_import(sparkContext._jvm, "myPackage.MyPythonGateway")
myPythonGateway = spark.sparkContext._jvm.MyPythonGateway()
myPythonGateway.findMyNum("1234 GOOD DAY")
specify the jar containing myPackage.MyPythonGateway with --jars option in spark-submit
If input.map(f) has inputs as an RDD for example, this might work, since you can't access the JVM variable (attached to spark context) inside the executor for a map function of an RDD (and to my knowledge there is no equivalent for #transient lazy val in pyspark).
def pythonGatewayIterator(iterator):
results = []
jvm = py4j.java_gateway.JavaGateway().jvm
mygw = jvm.myPackage.MyPythonGateway()
for value in iterator:
results.append(mygw.findMyNum(value))
return results
inputs.mapPartitions(pythonGatewayIterator)
all you need to do is compile jar and add to pyspark classpath with --jars or --driver-class-path spark submit options. Then access class and method with below code-
sc._jvm.com.company.MyClass.func1()
where sc - spark context
Tested with Spark 2.3. Keep in mind, you can call JVM class method only from driver program and not executor.
Has anyone attempted to "link" in the Rascal command line jar in a java executable and call REPL commands from this java executable?
I found a similar question on stackoverflow (Running a Rascal program from outside the REPL), but that doesn't go into details unfortunately.
I also checked the Rascal tutor site, but couldn't find any examples on how to do this. Tijs told me that it's something along the lines of "instantiate an interpreter and then call the import() function, after which the call() function can be called to inject REPL commands).
Is there any example code on how to do, e.g. the following from the tutor site on the REPL but from a java programming context instead of on the command line:
rascal>import demo::lang::Exp::Concrete::NoLayout::Syntax;
ok
rascal>import ParseTree;
ok
rascal>parse(#Exp, "2+3");
sort("Exp"): `2+3`
The following would do the trick; a utility class for the same can be found in rascal/src/org/rascalmpl/interpreter/JavaToRascal.java:
GlobalEnvironment heap = new GlobalEnvironment();
IValueFactory vf = ValueFactoryFactory.getValueFactory();
TypeFactory TF = TypeFactory.getInstance();
IRascalMonitor mon = new NullRascalMonitor();
Evaluator eval = new Evaluator(vf, new PrintWriter(System.err), new PrintWriter(System.out), new ModuleEnvironment(ModuleEnvironment.SHELL_MODULE, heap), heap);
eval.doImport(mon, "demo::lang::Exp::Concrete::NoLayout::Syntax");
eval.doImport(mon, "ParseTree");
eval.eval(mon, "parse(#Exp, \"2+3\");", URIUtil.rootLocation("unknown"));
There is also more efficient ways of interacting with the evaluator, via the pdb.values IValue interfaces to build data and ICalleableValue to call Rascal functions. You can use the above heap object to query its environments to get references to functions and you can use the low level pdb.values API to construct values to pass to these functions.
Caveat emptor: this code is "internal" API with no guarantee for backward compatibility. I can guarantee that something like this will always be possible.
I have a Ruby script that I'd like to run at the startup of my Java program.
When you tell the ScriptEngine to evaluate the code for the first time, it takes a while. I'm under the impression that the reason it takes this long is because it first needs to compile the code, right?
I found that you can compile Ruby code, and then evaluate it later. The evaluation itself is fast - the compilation part is the slow one. Here I am compiling:
jruby = new ScriptEngineManager().getEngineByName("jruby");
Compilable compilingEngine = (Compilable)jruby;
String code = "print 'HELLO!'";
CompiledScript script;
script = compilingEngine.compile(code);
This snippet is what takes a while. Later when you evaluate it, it is fine.
So of course, I was wondering if it would be possible to "save" this compiled code into a file, so in the future I can "load" it and just execute it without compiling again.
As others have said, this is not possible with CompiledScript. However, with JRuby you have another option. You can use the command line tool jrubyc to compile a Ruby script to Java bytecode like so:
jrubyc <scriptname.rb>
This will produce a class file named scriptname.class. You can run this class from the command line as if it were a normal class with a main(String[] argv) method (note: the jruby runtime needs to be in the classpath) and you can of course load it into your application at runtime.
You can find more details on the output of jrubyc here: https://github.com/jruby/jruby/wiki/JRubyCompiler#methods-in-output-class-file
According to this, no.
"Unfortunately, compiled scripts are not, by default, serializable, so they can't be pre-compiled as part of a deployment process, so compilation should be applied at runtime when you know it makes sense."
I think some really easy cache will solve your problem:
class CompiledScriptCache {
static {
CompiledScriptCache INSTANCE = new CompiledScritCache();
}
publich static CompiledScriptCache get(){
retrun INSTANCE;
};
List<CompiledScript> scripts = new ArrayList<>();
public CompiledScript get(int id){
return scripts.get(id);
}
public int add(String code){
ScriptEngine jruby = new ScriptEngineManager().getEngineByName("jruby");
Compilable compilingEngine = (Compilable)jruby;
CompiledScript script;
script = compilingEngine.compile(code);
scripts.add(script);
return scripts.size()-1;
}
}
update
I thought this question was about avoiding to comile the source more than once.
Only other approach I could imagine is to create Java-Classes and make a cross-compile:
https://github.com/jruby/jruby/wiki/GeneratingJavaClasses