Fork me on GitHub
Fork me on GitHub

Java调用Python脚本

环境和问题

Maven依赖:

1
2
3
4
5
<dependency>
<groupId>org.python</groupId>
<artifactId>jython-standalone</artifactId>
<version>2.7.1</version>
</dependency>

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class InterpreterExample {
PythonInterpreter interpreter = null;
public InterpreterExample(){
PythonInterpreter.initialize(System.getProperties(),
System.getProperties(), new String[0]);
this.interpreter = new PythonInterpreter();
}
void execfile( final String fileName )
{
this.interpreter.execfile(fileName);
}
PyInstance createClass( final String className, final String opts ){
return (PyInstance) this.interpreter.eval(className + "(" + opts + ")");
}
public static void main(String[] args){
//PySystemState sys = Py.getSystemState();
//sys.path.add("/Library/Python/2.7/site-packages");
InterpreterExample ie = new InterpreterExample();
ie.execfile("./pythonSrc/environment.py");
PyInstance env = ie.createClass("Environment", "None");
env.invoke("get_os_version");
}
}

问题:

原因:https://stackoverflow.com/questions/9100909/using-multiprocessing-2-6-2-1-package-in-jython

1
2
3
You can't use it if multiprocessing requires C extensions i.e., if you can't disable them and the module was not reimplemented for Jython in Java/pure Python. multiprocessing module is included in the stdlib since Python 2.6. Current Jython supports Python 2.5.
There is no GIL in Jython so you can use threading in many cases where you would use multiprocessing in CPython.

此外Jython还有其他问题:
Python执行时的sys.path和Jython的sys.path路径不一致。两者加载类库的路径不一样。
代码:

1
2
3
4
5
6
7
8
import org.python.util.PythonInterpreter;
public class HelloPython {
public static void main(String[] args) {
PySystemState sys = Py.getSystemState();
interpreter.execfile("./pythonSrc/test_sys.py");
}
}

test_sys.py代码:

1
2
import sys
print(sys.path)

打印出来的是:

1
['/Users/jkzhao/Documents/work/repository/org/python/jython-standalone/2.7.1/Lib', '/Users/jkzhao/Documents/work/repository/org/python/jython-standalone/2.7.1/jython-standalone-2.7.1.jar/Lib', '__classpath__', '__pyclasspath__/']

直接执行test_sys.py打印sys.path:

1
['/Users/jkzhao/Documents/study/Java/IDEAProjects/call', '/Library/Python/2.7/site-packages/pip-7.1.0-py2.7.egg', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python27.zip', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-old', '/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload', '/System/Library/Frameworks/Python.framework/Versions/2.7/Extras/lib/python/PyObjC', '/Library/Python/2.7/site-packages']

常见的java调用python脚本方式

1.通过Jython.jar提供的类库实现:上面使用的就是这种方式
2.通过Runtime.getRuntime()开启进程来执行脚本文件
介绍下第2种方式:
代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public class Cmd {
public static void main(String[] args) throws IOException, InterruptedException {
try {
// using the Runtime exec method:
String[] arguments = new String[] { "python", "./pythonSrc/environment.py" };
Process p = Runtime.getRuntime().exec(arguments);
BufferedReader stdInput = new BufferedReader(
new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new
InputStreamReader(p.getErrorStream()));
// read the output from the command
System.out.println("Here is the standard output of the command:\n");
String line = null;
while ((line = stdInput.readLine()) != null) {
System.out.println(line);
}
// read any errors from the attempted command
System.out.println("\nHere is the standard error of the command (if any):\n");
String errline = null;
while ((errline = stdError.readLine()) != null) {
System.out.println(errline);
}
stdInput.close();
stdError.close();
int ret = p.waitFor();
System.out.println("Process exit code: " + ret);
if (ret != 0) {
System.out.println("do something");
}
// System.exit(0);
} catch (Exception e) {
System.out.println("exception happened - here's what I know: ");
e.printStackTrace();
System.exit(-1);
}
System.out.println("=============");
}
}

这样就正常执行了。
【注意】:
Runtime.exec方法将产生一个本地的进程,并返回一个Process子类的实例,该实例可用于控制进程或取得进程的相关信息。由于调用Runtime.exec方法所创建的子进程没有自己的终端或控制台,因此该子进程的标准IO(如stdin,stdou,stderr)都通过 p.getOutputStream(), p.getInputStream(), p.getErrorStream() 方法重定向给它的父进程了。用户需要用这些stream来向子进程输入数据或获取子进程的输出。
但是向标准输出流和标准错误流写数据,而JVM却不读取,数据会暂存在linux缓存区,当缓存区存满之后导致该进程无法继续写数据,会僵死,导致java进程会卡死在waitFor()处。我这里采取的办法是:java进程在waitFor()前不断读取标准输出流和标准错误流。

参考资料:
Running system commands in Java applications
用Java执行Python:Jython踩坑笔记
Java 调用外部命令使用 waitFor() 方法阻塞或锁死
Java执行shell遇到的各种问题