JPype Passing args to Java - 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()

Related

Passing arguments by running a python script to a jar

I have a simple Java app that does an addition by passing 2 arguments when running it. Here is the code:
package test_python;
import java.util.concurrent.TimeUnit;
public class Test_python {
public int addition(int first, int second) {
return first + second;
}
public static void main(String[] args) {
Test_python a = new Test_python();
System.out.println(a.addition(Integer.parseInt(args[0]), Integer.parseInt(args[1])));
}
}
And I have a python script :
import subprocess
import sys
first_arg = subprocess.check_output([sys.argv])
second_arg = subprocess.check_output([sys.argv])
subprocess.call(['java', '-jar', 'test_python.jar ',first_arg,second_arg])
I would like to pass two arguments like 2 and 3 to the python script and the to send the arguments to the jar and return the response. I get this error when I try:
Traceback (most recent call last):
File "C:\Certificat python\start-stop.py", line 5, in <module>
first_arg = subprocess.check_output([sys.argv])
File "C:\Users\40723\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line
424, in check_output
return run(*popenargs, stdout=PIPE, timeout=timeout, check=True,
File "C:\Users\40723\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line
505, in run
with Popen(*popenargs, **kwargs) as process:
File "C:\Users\40723\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line
951, in __init__
self._execute_child(args, executable, preexec_fn, close_fds,
File "C:\Users\40723\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line
1360, in _execute_child
args = list2cmdline(args)
File "C:\Users\40723\AppData\Local\Programs\Python\Python39\lib\subprocess.py", line
565, in list2cmdline
for arg in map(os.fsdecode, seq):
File "C:\Users\40723\AppData\Local\Programs\Python\Python39\lib\os.py", line 822, in
fsdecode
filename = fspath(filename) # Does type-checking of `filename`.
TypeError: expected str, bytes or os.PathLike object, not list
Can you please give me a solution ?
Thanks in advance
The cause of the error is because sys.argv is already a list e.g. ["script.py", "2", "3"] and you are wrapping it in another list thus making it [["script.py", "2", "3"]].
first_arg = subprocess.check_output([sys.argv])
Removing the outer list-wrapper and also removing the first item which is the script itself would remove the error.
first_arg = subprocess.check_output(sys.argv[1:])
But I doubt that is what you need, because subprocess.check_output runs the command, it doesn't get the arguments as what you are trying to do.
$ python script.py cat script.py # <cat> is a linux command that prints the file
b'#!/usr/bin/env python\n\nimport subprocess\nimport sys\n\n\nresult = subprocess.check_output(sys.argv[1:])\nprint(result)\n'
If what you want is to just get the values of the first and second argument e.g. 2 and 3 if invoked as python script.py 2 3, you can just do:
first_arg = sys.argv[1]
second_arg = sys.argv[2]

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;

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)

JRuby class and Java Scripting Engine unexpected results

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"));

Batch file renaming – inserting text from a list (in Python or Java)

I'm finishing a business card production flow (excel > xml > indesign > single page pdfs) and I would like to insert the employees' names in the filenames.
What I have now:
BusinessCard_01_Blue.pdf
BusinessCard_02_Blue.pdf
BusinessCard_03_Blue.pdf (they are gonna go up to the hundreds)
What I need (I can manipulate the name list with regex easily):
BusinessCard_01_CarlosJorgeSantos_Blue.pdf
BusinessCard_02_TaniaMartins_Blue.pdf
BusinessCard_03_MarciaLima_Blue.pdf
I'm a Java and Python toddler. I've read the related questions, tried this in Automator (Mac) and Name Mangler, but couldn't get it to work.
Thanks in advance,
Gus
Granted you have a map where to look at the right name you could do something like this in Java:
List<Files> originalFiles = ...
for( File f : originalFiles ) {
f.renameTo( new File( getNameFor( f ) ) );
}
And define the getNameFor to something like:
public String getNameFor( File f ) {
Map<String,String> namesMap = ...
return namesMap.get( f.getName() );
}
In the map you'll have the associations:
BusinessCard_01_Blue.pdf => BusinessCard_01_CarlosJorgeSantos_Blue.pdf
Does it make sense?
In Python (tested):
#!/usr/bin/python
import sys, os, shutil, re
try:
pdfpath = sys.argv[1]
except IndexError:
pdfpath = os.curdir
employees = {1:'Bob', 2:'Joe', 3:'Sara'} # emp_id:'name'
files = [f for f in os.listdir(pdfpath) if re.match("BusinessCard_[0-9]+_Blue.pdf", f)]
idnumbers = [int(re.search("[0-9]+", f).group(0)) for f in files]
filenamemap = zip(files, [employees[i] for i in idnumbers])
newfiles = [re.sub('Blue.pdf', e + '_Blue.pdf', f) for f, e in filenamemap]
for old, new in zip(files, newfiles):
shutil.move(os.path.join(pdfpath, old), os.path.join(pdfpath, new))
EDIT: This now alters only those files that have not yet been altered.
Let me know if you want something that will build the the employees dictionary automatically.
If you have a list of names in the same order the files are produced, in Python it goes like this untested fragment:
#!/usr/bin/python
import os
f = open('list.txt', 'r')
for n, name in enumerate(f):
original_name = 'BusinessCard_%02d_Blue.pdf' % (n + 1)
new_name = 'BusinessCard_%02d_%s_Blue.pdf' % (
n, ''.join(name.title().split()))
if os.path.isfile(original_name):
print "Renaming %s to %s" % (original_name, new_name),
os.rename(original_name, new_name)
print "OK!"
else:
print "File %s not found." % original_name
Python:
Assuming you have implemented the naming logic already:
for f in os.listdir(<directory>):
try:
os.rename(f, new_name(f.name))
except OSError:
# fail
You will, of course, need to write a function new_name which takes the string "BusinessCard_01_Blue.pdf" and returns the string "BusinessCard_01_CarlosJorgeSantos_Blue.pdf".

Categories