このBlogは移転しました。今後は aish.dev を御覧ください。

JCCを試してみる

JCC は、 Javaのライブラリを Python から呼び出すため拡張モジュールを生成してくれるツールで、全文検索ライブラリ Apache LucenePython インターフェースである PyLucene 用に開発された。しかし決して PyLucene 専用のツールではなく、Python から Javaを呼び出す必要がある場合に利用できる汎用的なツールである。

とりあえず使ってみる

まずこんな感じの Java クラスを定義してみた。

package hellojcc;
import java.io.*;

public class HelloJCC {
    public String msg;
    public HelloJCC(String s) {
        msg = s;
    }
    public void sayHello() {
        System.out.println(msg);
    }
}

これを hellojccパッケージに格納し、hellojcc.jar を作成しておく。で、jcc で次のようにしてラッパを作成する。

python -m jcc.__main__ 
    --jar hellojcc.jar   # hellojcc.jar からラッパを作成してね
    --package java.lang  # java.lang.* のラッパも作ってね
    --python pyhellojcc  # ラッパモジュールの名前は pyhellojcc にしてね
    --version 0.0.1      # ラッパモジュールのバージョンは 0.0.1だからね
    --build              # あ、ビルドしといてね
    --install            # ついでにインストールもお願いね

distutilsかsetuptoolsがインストールされていれば、Jarファイルからラッパを作り、インストールまで済ませてくれるお利口さんツールである。これを Pythonから呼び出してみよう。

import pyhellojcc
pyhellojcc.initVM()
shello = pyhellojcc.HelloJCC("test")
shello.sayHello()

簡単だ。jccで生成したモジュールを利用するスレッドでは、使い始める前にかならず pyhellojcc.initVM() を呼び出さなければならないので注意。

pyhellojccにはどんなオブジェクトが定義されているのだろうか? dir(pyhellojcc)としてみると、こんな結果が返ってくる。

['AbstractStringBuilder', 'Appendable', 'Boolean', 'CLASSPATH', 
'CharSequence', 'Class', 'ClassLoader', 'ClassNotFoundException',
'Comparable', 'ConstVariableDescriptor', 'Double', 'Enumeration',
'Exception', 'FinalizerClass', 'FinalizerProxy', 'HelloJCC',
'IllegalAccessException', 'IllegalArgumentException',
'InstantiationException', 'Integer', 'InterruptedException',
'InvalidArgsError', 'Iterator', 'JArray', 'JArray_bool', 
'JArray_byte', 'JArray_char', 'JArray_double', 'JArray_float',
'JArray_int', 'JArray_long', 'JArray_object', 'JArray_short',
'JArray_string', 'JCCEnv', 'JObject', 'JavaError', 'Long',
'Number', 'NumberFormatException', 'Object', 'Package', 
'PrintWriter', 'RuntimeException', 'SecurityException', 
'StackTraceElement', 'String', 'StringBuffer', 'StringBuilder',
'StringWriter', 'Throwable', 'VERSION', 'Writer', '__builtins__',
 '__dir__', '__doc__', '__file__', '__name__', '__package__',
 '__path__', '_pyhellojcc', 'findClass', 'getVMEnv', 'initVM', 'os']

HelloJCC の他に、jccのオプションで指定した java.lang 以下のクラスもちゃんと使えるようになっている。

オーバーロードは?

Javaのメンバがオーバーロードされている場合はどうなるのだろうか? HelloJCCクラスをこんな感じに書き換えてみる。

public class HelloJCC {
    public HelloJCC(String s) {
        System.out.println(s);
    }
    public HelloJCC(Integer i) {
        System.out.println(i);
    }
}

もう一度 pyhellojcc を作成して実験してみると:

c:\src\test>python
Python 2.6.2 (r262:71600, Oct  9 2009, 00:10:55) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import pyhellojcc
>>> pyhellojcc.initVM()
<jcc.JCCEnv object at 0x01A4B150>
>>> h = pyhellojcc.HelloJCC(u"あいうえお")
あいうえお
>>> h = pyhellojcc.HelloJCC(100)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
pyhellojcc.InvalidArgsError: (<type 'HelloJCC'>, '__init__', (100,))
>>> n = pyhellojcc.Integer(100)
>>> h = pyhellojcc.HelloJCC(n)
100

数値->java.lang.Integer の自動変換はやってくれないみたいだけど、自分で Integer() のインスタンスを作成すれば正しくオーバーロードも解決してくれた。

もう少し複雑な例

もう一つクラスを定義する。

package hellojcc;
public class HelloArg {
    String arg;
    public HelloArg() {
    }
    public void setArg(String s) {
        arg = s;
    }
    public String getArg() {
        return arg;
    }
}

HelloJCC もこんな感じにしてみる。

public class HelloJCC {
    public String msg;
    public Integer imsg;
    public HelloArg arg;
    
    public HelloJCC(String s) {
        msg = s;
    }
    public HelloJCC(Integer i) {
        imsg = i;
    }
    public HelloJCC(HelloArg a) {
        arg = a;
    }
    public void sayHello() {
        System.out.println(getObj());
    }
    public Object getObj() {
        if (msg != null) {
            return msg;
        } else if (imsg != null) {
            return imsg;
        } else if (arg != null) {
            return arg;
        }
        throw new RuntimeException("No arg!");
    }
}

するとこんな感じで実行することができる

>>> shello = pyhellojcc.HelloJCC("test")
>>> shello.sayHello()
test
>>> ihello = pyhellojcc.HelloJCC(pyhellojcc.Integer(123))
>>> ihello.sayHello()
123
>>> a = pyhellojcc.HelloArg()
>>> a.setArg("abcdefg")
>>> ahello = pyhellojcc.HelloJCC(a)
>>> ahello.sayHello()
hellojcc.HelloArg@e0e1c6


HelloJCC.getObj()で、Object型が返ってきたらどうなるのだろうか?

>>> print ahello.getObj()
hellojcc.HelloArg@e0e1c6
>>> print ahello.getObj().getArg()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Object' object has no attribute 'getArg'

自動的には HelloArg 型への変換をしてくれないみたいだ。Java のクラスには cast_() というメソッドが定義されていて、これを使えば目的の型に変換できるようになっている。

>>> arg = ahello.getObj()
>>> type(arg)
<type 'Object'>
>>> arg2 = pyhellojcc.HelloArg.cast_(arg)
>>> type(arg2)
<type 'HelloArg'>
>>>

とりあえずここまで

しばらくいじくってみたが、PythonからJavaを利用するためのツールとして十分実用可能だ。すべてを動的に処理するのではなく、一旦C++のソースを生成してからモジュールを構築するので、不明な点があっても簡単に動作を調べられるのも気に入った。InfoPile 用に組み込んでみようと思っている。