【问题标题】:PyLucene JCC: implement a Java interface in python and receive Java thread callbacks through itPyLucene JCC:在python中实现一个Java接口并通过它接收Java线程回调
【发布时间】:2015-01-12 13:17:21
【问题描述】:

我正在玩我的新玩具 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)

除了 null 侦听器实例之外,CPython 是否可以执行类似的操作?我已经读到,在它的实现中,一次只有一个线程可以执行 python 脚本,这可能(?)对我来说是个问题。用 Jython 做这样的事情是微不足道的。

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

【问题讨论】:

    标签: java python python-2.7 pylucene jcc


    【解决方案1】:

    想通了。您需要为 java 类定义一个 pythonic 扩展来完成这项工作。详细过程在JCC documentation在 Python 中编写 Java 类扩展)中有描述,相当简单。

    首先,编写一个实现您的接口的类,并添加一些 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);
    
    }
    

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

    然后像往常一样生成包装器:

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

    最后为新类做一个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
    

    【讨论】:

      猜你喜欢
      • 2012-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多