使用JPype实现Python调用JAVA程序

来源:互联网 发布:韩宜淘宝旗舰店 编辑:程序博客网 时间:2024/05/18 22:55
BTCC给的程序化交易例子是用的Java语言的Fix金融交换协议,QuickFix的C++代码和python调用quickfix库在建立连接时有一些问题没有弄明白,以后有时间了再看,现在只能先使用BTCC给的java代码来获取专业交易的市场数据以及使用这个Java代码来进行交易下单。无奈之下找到了一个python调用Java程序的库Jpype。这里转载收藏一下。之所以要用python,是因为python简洁,加上之前写的现货交易程序是用python的websocket写的,而且python也用习惯了。其实用纯java也没有什么问题。

转自:http://xiaojizhuomi.iteye.com/blog/1154108

一, Ubuntu下环境的配置

1,安装JDK

首先安装java的jdk,我是从新立得软件包中安装:

Java代码  收藏代码
  1. sudo apt-get install sun-java6-jdk      (jdk的版本取决于你的ubuntu版本)  

 安装后的jdk的目录是:/usr/lib/jvm/java-6-sun-1.6.0.26/。安装好jdk后,最重要的就是配置环境变量。环境变量的配置有好几种方法,能设置环境变量的文件包括:/etc/profile/etc/evironment ~/.profile/bashrc~/.bashrc。这几个文件里定义的环境变量,用户使用的权限和执行的时间不一样。在/etc/environment文件里定义jdk环境变量。在该文件中添加:

 

Java代码  收藏代码
  1. JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.26  
  2. CLASSPATH=.:/usr/lib/jvm/java-6-sun-1.6.0.26/lib  

   (CLASSPATH以冒号分隔开。. 号表示当前目录)

然后在~/.bashrc文件中添加:

Java代码  收藏代码
  1. export JAVA_HOME=/usr/lib/jvm/java-6-sun-1.6.0.26  
  2. export CLASSPATH=.:$JAVA_HOME/lib  

因为ubuntu默认安装了OPENJDK所以要选择所用的JDK执行下面两条命令:

 

Java代码  收藏代码
  1. update-alternatives --install /usr/bin/java java /usr/lib/jvm/java-6-sun-1.6.0.26/bin/java 300  
  2. update-alternatives --install /usr/bin/javac javac /usr/lib/jvm/java-6-sun-1.6.0.26/bin/javac 300  

  这样安装的java就可以加入java选单。再调用下面的命令就可以选择java的版本了。

Java代码  收藏代码
  1. update-alternatives --config java  

 

在终端测试一下吧!到此jdk就安装完成了。

 

2.安装Jpype

从官网上下载JPype。在unbuntu下安装还需要做点小修改,修改setup.py,文件中setupLinux函数中的self.javaHome变量为自己的JDK目录:

Java代码  收藏代码
  1. self.javahome='usr/lib/jvm/java-6-sun-1.6.0.26‘  

运行python setup.py install.的话 可能会出现找不到Python.py。需要安装python-dev。如果再安装出错的话,就需要你自己排错了。当然你在安装JPype之前必须安装好Python。测试一下在终端中打开python shellimport Jpype。无错表示安装成功。

 

二 python调用java 示例

 

写个很简单的java程序并打包成jar包供python程序调用。

简单的java程序:

 

Java代码  收藏代码
  1. package test;  
  2.   
  3. public class TestApi  
  4. {  
  5.     public String getData(String key){  
  6.         return key;  
  7.     }  
  8.     public void printData(String key){  
  9.         System.out.println(key);  
  10.     }  
  11.       
  12.     public static void main(String[] args)  
  13.     {  
  14.         System.out.println(123);  
  15.     }  
  16. }  

编译成.class文件:

 

Java代码  收藏代码
  1. javac TestApi.java  

将.class文件放入test文件夹中,在test文件夹外新建一个.mf文件,就叫TestApi.mf吧。并在该文件中加入:

Java代码  收藏代码
  1. Created-By: Jesse Main-Class: test.TestApi  

  Main-Class指的就是主程序的入口类,利用下列指令打包jar文件:

 

Java代码  收藏代码
  1. jar cvfm TestApi.jar TestApi.mf test  

 到此,java的包文件已经完成,下面就测试JPype吧:

 

Python代码  收藏代码
  1. #!/usr/bin/env python  
  2. #coding : utf-8  
  3.   
  4. from jpype import *  
  5.   
  6. jvmpath = getDefaultJVMPath()  
  7. startJVM(jvmpath, "-ea""-Djava.class.path=.")  
  8. TA = JPackage('test').TestApi  
  9. jd = TA()  
  10. jd.printData('1234')  
  11. s = jd.getData('a')  
  12. print s  
  13. shutdownJVM();  
 
JPype使用要点
转自:http://www.elias.cn/Python/JPype

在Python中调用Java类库和Java代码的桥接解决方案,背后使用的机制是JNI。虽然看起来这个库近年来更新不够活跃,原作者也试图用一个叫JEmbed(还没有发布)来替代之,但就我个人的使用体会来讲,现有版本是稳定可用的。

感谢Limodou~正是当初搜到了他在ChinaUnix上的旧帖子,才知道了jpype~

  • 官方网站:http://jpype.sourceforge.net/
  • 官方使用文档:http://jpype.sourceforge.net/doc/user-guide/userguide.html

1.? 基本安装

经试验,0.5.3版本不需要特殊处理,直接python setup.py install就可以了(安装过程中显示的warning可以不必理会)。用于测试是否安装成功的Hello World

from jpype import *
startJVM(getDefaultJVMPath())
java.lang.System.out.println("hello world")
shutdownJVM()

2.? 启动JVM

依靠startJVM这个函数来完成,一个使用的例子是这样的:

vmPath = jpype.getDefaultJVMPath()
jpype.startJVM(vmPath, "-Xms32m""-Xmx256m""-mx256m""-Djava.class.path=/home/some-lib.jar:")

startJVM的第一个参数是JVM库所在的路径(和JAVA_HOME不是一回事儿),通常可以用jpype.getDefaultJVMPath()来自动获取系统默认JVM的路径。如果系统中安装了多个JDK,希望从中选择一个,则可以手动注明这个路径。比如Mac OSX下可以写成"/System/Library/Frameworks/JavaVM.framework/Libraries/libjvm_compat.dylib"

剩下的都是发送给JVM的启动参数,每个逗号见是一个参数。因为这里是不支持带空格的参数写法的,所以例子里特意把classpath参数写成了-Djava.class.path=...的形式。注意这里需要手工保证参数的正确性,jpype是不会对错误的参数给出提示的,它的反应很简单,就是在后面用到这个JVM的时候报一些怎么也想不明白的错误……所以,使用jpype遇到任何问题,首先检查传给startJVM的各参数正确性。

3.? 如何调用一个Java函数

主要靠JPackage语句来实现,比如

Document = jpype.JPackage('org').w3c.dom.Document

可以把Java里面的org.w3c.dom.Document映射给Python里面的Document变量。

java和javax两个包不需要以这种方式来调用,直接类似jpype.java.lang.System.out.println()这样就可以了。

有时候我们会遇到类似"TypeError: Package org.w3c.dom.Document is not Callable"这样的错误。通常这时用到的Java指令在jar里面,而这个jar没有被正确导入,所以JVM找不到它。也就是说,遇到这种错误时,要去检查startJVM函数中的-Djava.class.path=参数的设置,通常都是因为这里的路径写错了造成的。

4.? 如果捕捉Java异常

可以在Python里使用 jpype.JavaException指代所有的Java异常,比如像下面这样:
import jpype
jpype.startJVM(jpype.getDefaultJVMPath())
try:
    jpype.java.lang.Integer("x1")
except jpype.JavaException, ex:
    print ex.javaClass(), ex.message()
    print ex.stacktrace()    
jpype.shutdownJVM()

如果要捕获特定的Java异常呢,则需要用到jpype.JException,比如像下面这样捕获的就是java.lang.NumberFormatException

import jpype
jpype.startJVM(jpype.getDefaultJVMPath())
try:
    jpype.java.lang.Integer("x1")
except jpype.JException(jpype.java.lang.NumberFormatException), ex:
    print ex.javaClass(), ex.message()
    print ex.stacktrace()    
jpype.shutdownJVM()

5.? 如何处理Java的函数多态

Java里面是允许参数格式不同的多个同名函数的,Python里面则不允许。这样在通过jpype调用Java api里面的函数时,有时会因为参数的类型乱掉而报错。那么怎么能调用到Java里面的特定函数呢?没办法,做强制类型转换吧。

比如jpype.java.lang.System.out.println(1)实际会调用println(int),那么如果我们想调用println(byte)%,则可以写成jpype.java.lang.System.out.println(JByte(1))这样。。

6.? 如何重启JVM

jpype提供的shutdownJVM()方法实际调用的是JNI接口的unload实现,但是Sun对unload的实现有点问题,造成的结果就是jpype调用shutdownJVM()以后就没法再startJVM()了(会报错)。什么?您问关掉JVM干嘛还要重新开启它,这个折腾个什么劲?答案很简单:因为有时在未知的黑暗角落隐藏着邪恶的源头——内存泄露。。

既然jpype没法重启JVM,那么只好把jpype放到processing里面来用,需要重启时,就杀掉当前进程,重新启动一个新进程好了。。(processing安装很简单,Python 2.6官方发行版已经带了,之前的版本则可以easy_install processing)

下面给出一个processing下用jpype的例子:

import jpype
import processing

def java_loop(pipe, id):
    jpype.startJVM(jpype.getDefaultJVMPath())
    while True:
        jpype.java.lang.System.out.println(pipe.recv() + ' ' + id)
        pipe.send(None)

head1, head2 = processing.Pipe()
p = processing.Process(target = java_loop, args = [head2, '(JVM 1)'])
p.setDaemon(True)
p.start()
head1.send("Hello message from")
head1.recv()
p.terminate()

head1, head2 = processing.Pipe()
p = processing.Process(target = java_loop, args = [head2, '(JVM 2)'])
p.setDaemon(True)
p.start()
head1.send("Hello message from")
head1.recv()

这里用到的pipe.recv()要小心,一旦阻塞可能会与twisted之类的框架产生冲突。一个可能的解决办法是在recv()之前用pipe.poll()函数检测一下管道里面是否有待接收数据,如果没有就等一会重新poll()就是了。(poll()自称是非阻塞的,因为它只阻塞当前线程;而recv()则会阻塞当前进程,于是twisted就不干了^_^)

Python+Jpype 调用 Java 碰壁全记录
转自:http://blog.csdn.net/newxren/article/details/7351200

因为在Jython 调用 Java 碰壁全纪录中已经有了“出色”碰壁经历,此处仿照其大致过程

命令行下无差异,Eclipse中略有差异(classpath的设置方式不同所致)

 JavaClass 的定义
public class JavaClass {
    private String str = "";
    public JavaClass() {
        this.str = "JavaClass Init";
    }
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }
}

 

一、命令行模式

 

1、进入 python 目录,查看当前目录下只有一个JavaClass.java文件

D:\>cd python
D:\python>dir
2012-03-13  09:59    <DIR>          .
2012-03-13  09:59    <DIR>          ..
2012-03-13  09:59               227 JavaClass.java
               1 个文件            227 字节
               2 个目录 37,943,169,024 可用字节
               
2、启动Python命令行模式(Python版本是 2.7)

D:\python>python

 

3、导入 JavaClass时,提示找不到JavaClass模块

>>> from jpype import *
>>> jvmPath = getDefaultJVMPath()
>>> startJVM(jvmpath)
>>> startJVM(jvmPath)
>>> JavaClass = JClass("JavaClass")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Python27\lib\site-packages\jpype\_jclass.py", line 54, in JClass
    raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class JavaClass not found

原因:Python中导入的是.class文件,而JavaClass.java尚未编译生成 .class文件

 

4、Ctrl-Z退出 Python, 编译 JavaClass.java

D:\python>javac JavaClass.java
D:\python>dir
2012-03-13  10:12    <DIR>          .
2012-03-13  10:12    <DIR>          ..
2012-03-13  10:12               443 JavaClass.class
2012-03-13  09:59               227 JavaClass.java
               2 个文件            670 字节
               2 个目录 37,943,169,024 可用字节

 

5、按第2步,再次进入Python模式后导入 JavaClass时未报异常,说明成功导入,简单调用情况如下

D:\python>python

>>> from jpype import *
>>> jvmPath = getDefaultJVMPath()
>>> startJVM(jvmpath)
>>> startJVM(jvmPath)
>>> JavaClass = JClass("JavaClass")
>>> jc = JavaClass()
>>> jc.str   // 调用get方法
'JavaClass Init'
>>> jc.str = "JavaClass Set"   // 调用set方法
>>> jc.str
'JavaClass Set'
 

 

二、eclipse IDE 环境

workplace 是d:\python,项目名称是mython,其结构如下

mython
    ......
    bin
        javaDemo
            JavaClass.class
        pythonDemo
            Py2Ja.py
    src
        javaDemo
            JavaClass.java
        pythonDemo
            Py2Ja.py

 

1、JavaClass.java同上(但是第一行多了一个package javaDemo的定义)

 

2、编辑Py2Ja.py如下:

from jpype import *
jvmPath = getDefaultJVMPath()
startJVM(jvmPath)
JavaClass = JClass("javaDemo.JavaClass")
jc = JavaClass()
print jc.str
jc.str = "JavaClass IDE"
print jc.str
shutdownJVM()

 

测试运行时报异常
Traceback (most recent call last):
  File "D:\python\mython\src\pythonDemo\Py2Ja.py", line 4, in <module>
    JavaClass = JClass("javaDemo.JavaClass")
  File "C:\Python27\lib\site-packages\jpype\_jclass.py", line 54, in JClass
    raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class javaDemo.JavaClass not found

原因:找不到javaDemo是因为“当前路径”下找不到JavaDemo文件或者目录

解决方法:手工添加JVM的启动参数-Djava.class.path ='D:\python\Mython\src

 

3再次修改后如下:

from jpype import *
jvmPath = getDefaultJVMPath()
classPath = "f:\workplace\Python\src"
jvmArg = "-Djava.class.path="+classPath
startJVM(jvmPath, jvmArg)
JavaClass = JClass("javaDemo.JavaClass")
jc = JavaClass()
print jc.str
jc.str = "JavaClass IDE"
print jc.str
shutdownJVM()

 

测试运行时仍然报异常
Traceback (most recent call last):
  File "F:\workplace\Python\src\pythonDemo\Py2Ja.py", line 6, in <module>
    JavaClass = JClass("javaDemo.JavaClass")
  File "C:\Python27\lib\site-packages\jpype\_jclass.py", line 54, in JClass
    raise _RUNTIMEEXCEPTION.PYEXC("Class %s not found" % name)
jpype._jexception.ExceptionPyRaisable: java.lang.Exception: Class javaDemo.JavaClass not found

与2中的异常一样:找不到JavaClass。

原因:我们已经设置了-Djava.class.path ='D:\python\Mython\src,仍然找不到JavaClass,只能说明是找不到JavaClass.class文件

        在命令行中第3步中提到Python文件导入的应该是.class文件,在看前面mython项目的整个目录发现,class文件在mython\bin\javaDemo中。也就是说可能是因为编译器找到了JavaClass的定义即 JavaClass.java,但是找不到它的.class文件。

那java文件对应的class文件为什么不是在src包下的JavaDemo中,而是在bin包下的JavaDemo中?

        右击项目mython->Properties->Java Build Path->source最下面有default output folder中默认的是mython\bin,且这个目录在 Package Explorer视图下是看不到的,在Navigator视图中可以看到。

 

4、将default output folder 修改为 mython\src,目录结构变化如下

mython
    ......

    src
        javaDemo
            JavaClass.class
            JavaClass.java
        pythonDemo
            Py2Ja.py

 

测试运行成功:
JavaClass Init
JavaClass IDE
 

JVM activity report     :
 classes loaded       : 20
JVM has been shutdown

 

总结:对于新手不太习惯命令行式的Java程序调试,IDE虽然简单方便,但是都有自己默认的规则。这些规则新手可能注意不到,所以在IDE环境中照搬程序源码的时候,总会报一些奇奇怪怪的错误,使得本来简单的问题看上去很复杂。究其原因:1、对开发环境不熟悉、2、java基础不扎实。

 

特殊情况备注:

        1、***.py文件运行时,默认路径即该文件的所在路径,导入***.class时的路径就变成了 ***.py所在的包 + from中的包,即默认.class文件是在 ***.py所在的包 的 子包中,所以在调用Java文件时,需要特别添加class文件所在路径。即使Java文件和Python文件在同一个包中,Java文件仍然在某个包中,测试运行时仍然要设置java文件所在的包最上级所在的目录,一般为 src。

        2、将Java文件和Python文件都放在 src 下时,虽然目录结构中显示一个叫"(default package)",但是在Java文件的内部是没有包的设定的,这种情况下运行Python文件时,可不手工设置class文件所在路径,因为此时 Python文件的所在目录为src,而Java文件也在src中,且没有包名引起的问题。

 

刚刚接触 Python,因为基础不太扎实,期间诸多碰壁,仅此记录。

成长的道路上不怕跌倒,怕的是的跌倒了再也爬不起来。为自己加油!