PyLucene JCC:在python中实现一个Java接口,并通过它接收Java线程回调

PyLucene JCC: implement a Java interface in python and receive Java thread callbacks through it

我正在玩我的新玩具 JCC 2.21,但在 python 脚本中实现回调时遇到了问题。我包装了以下简单的 Java 线程 API 并从 python 2.7 (CPython) 调用它,但是当我调用 JccTest.addJccTestListener(JccTestListener) 方法时,JVM 报告一个空参数.

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class JccTest implements Runnable {

    private final Object listenersLock = new Object();
    private final List<JccTestListener> listeners = new ArrayList<JccTestListener>();
    private final AtomicBoolean running = new AtomicBoolean(false);
    private final AtomicBoolean finished = new AtomicBoolean(false);

    public void start() {
        if (running.compareAndSet(false, true)) {            
            new Thread(this).start();
        }
    }

    public void stop() {
        finished.set(true);
    }

    public void addJccTestListener(JccTestListener l) {
        if (l == null) {
            throw new IllegalArgumentException("argument must be non-null");
        }
        synchronized (listenersLock) {
            listeners.add(l);
        }
    }

    public void removeJccTestListener(JccTestListener l) {
        synchronized (listenersLock) {
            listeners.remove(l);
        }
    }

    @Override
    public void run() {     
        System.out.println("Start");

        while (!finished.get()) {
            System.out.println("Notifiying listeners");
            synchronized (listenersLock) {
                for (JccTestListener l : listeners) {
                    System.out.println("Notifiying " + String.valueOf(l));
                    l.message("I'm giving you a message!");
                }
            }
            System.out.println("Sleeping");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                continue;
            }
        }

        running.set(false);
        System.out.println("Stop");
    }

    public static void main(String[] args) throws InterruptedException {
        JccTest test = new JccTest();
        test.addJccTestListener(new JccTestListener() {

            @Override
            public void message(String msg) {
                // called from another thread
                System.out.println(msg);
            }
        });
        test.start();
        Thread.sleep(10000);
        test.stop();
    }
}

public interface JccTestListener {
    public void message(String msg);
}

生成的包装器:

python -m jcc --jar jcc-test.jar --python jcc_test --build --install

然后执行这个脚本(相当于JccTest的main方法):

import jcc_test
import time, sys

jcc_test.initVM(jcc_test.CLASSPATH)

test = jcc_test.JccTest()


class MyListener(jcc_test.JccTestListener):
    def __init__(self):
        pass

    def message(self, msg):
        print msg

test.addJccTestListener(MyListener())
test.start()
time.sleep(10)
test.stop()

sys.exit(0)

这导致:

"python.exe" jcc_test_test.py
Traceback (most recent call last):
  File "jcc_test_test.py", line 16, in <module>
    test.addJccTestListener(MyListener())
jcc_test.JavaError: java.lang.IllegalArgumentException: argument must be non-null
    Java stacktrace:
java.lang.IllegalArgumentException: argument must be non-null
    at com.example.jcc.JccTest.addJccTestListener(JccTest.java:32)

除了空侦听器实例之外,CPython 甚至可以做这样的事情吗?我读过,在它的实现中,一次只能有一个线程执行 python 脚本,这对我来说可能(?)是个问题。用 Jython 做这样的事情是微不足道的。

我对 python 比较陌生,所以请保持温柔。

想通了。您需要为 java class 定义一个 pythonic 扩展名才能使其工作。详细过程在JCC documentationWriting Java class extensions in Python)中描述,相当简单。

首先,编写实现您的界面的 class 代码,并添加一些 JCC 可识别并影响包装器生成器将生成的内容的魔法标记。

public class JccTestListenerImpl implements JccTestListener {

    // jcc specific
    private long pythonObject;

    public JccTestListenerImpl() {}

    @Override
    public void message(String msg) {
        messageImpl(msg);
    }

    // jcc specific
    public void pythonExtension(long pythonObject) {
        this.pythonObject = pythonObject;
    }

    // jcc specific
    public long pythonExtension() {
        return this.pythonObject;
    }

    // jcc specific
    @Override
    public void finalize() throws Throwable {
        pythonDecRef();
    }

    // jcc specific
    public native void pythonDecRef();

    public native void messageImpl(String msg);

}

标记由我的评论表示,并且必须逐字出现在任何 class 中,要在 python 中扩展。我的实现是将接口方法委托给一个原生的实现方法,在python.

中进行扩展

然后照常生成包装器:

python -m jcc --jar jcc-test.jar --python jcc_test --build --install

最后为新 class 做一个 python 扩展:

import jcc_test
import time, sys

jvm = jcc_test.initVM(jcc_test.CLASSPATH)

test = jcc_test.JccTest()


class MyListener(jcc_test.JccTestListenerImpl):
    ## if you define a constructor here make sure to invoke super constructor
    #def __init__(self):
    #    super(MyListener, self).__init__()
    #    pass

    def messageImpl(self, msg):
        print msg


listener = MyListener()
test.addJccTestListener(listener)
test.start()
time.sleep(10)
test.stop()

sys.exit(0)

随着回调的到​​来,这现在可以正常工作了。

"python.exe" jcc_test_test.py
Start
Notifiying listeners
Notifiying com.example.jcc.JccTestListenerImpl@4b67cf4d
I'm giving you a message!
Sleeping
Notifiying listeners
Notifiying com.example.jcc.JccTestListenerImpl@4b67cf4d
I'm giving you a message!
Sleeping

Process finished with exit code 0