2017-12-02 5 views
2

ich zur Zeit versucht, die folgenden mit Py4J zu tun:Py4J Rückruf von Java Runnable

  • Definieren Sie eine Methode ("Vollstrecker") in Python, die
  • JVM Methoden aufruft
  • eine Python definieren ("Rückruf") Objekt eine JVM Schnittstelle
  • Konstruiere eine JVM Objekt gegeben dieses Rückrufobjekt
  • Anruf Verfahren für dieses Objekt Implementierung, die den Rückruf auf dem Callback-Objekt einen neuen Thread in Java, nennen, laichen wird die wird (auf der Python Seiten) Ausführen der "Executor" -Methode

Hier ist, was ich auf der Java-Seite der Dinge haben:

package javabridge.test; 
public interface PythonCallback { 
    Object notify(Object source); 
} 


package javabridge.test; 
public class ScheduledRunnable implements Runnable { 
    private PythonCallback callback; 
    public ScheduledRunnable(PythonCallback callback) { 
     this.callback = callback; 
    } 
    @Override 
    public void run() { 
     System.out.println("[ScheduledRunnable] run -> notify"); 
     this.callback.notify(this); 
    } 
} 


package javabridge.test; 
import py4j.GatewayServer; 
public class Test { 
    private PythonCallback callback; 
    public Test(PythonCallback callback) { 
     this.callback = callback; 
    } 
    public void runSynchronous() { 
     System.out.println("[runSynchronous] run -> notify"); 
     this.callback.notify(this); 
    } 
    public void runAsynchronous() { 
     System.out.println("[runAsynchronous] run -> spawn thread"); 
     ScheduledRunnable runnable = new ScheduledRunnable(callback); 
     Thread t = new Thread(runnable); 
     t.start(); 
    } 
    public static void main(String[] args) { 
     GatewayServer server = new GatewayServer(); 
     server.start(true); 
    } 
} 

Auf der Seite Python, ich habe das folgende Skript:

from py4j.java_gateway import JavaGateway, java_import, get_field, CallbackServerParameters 
from py4j.clientserver import ClientServer, JavaParameters, PythonParameters 

gateway = JavaGateway(callback_server_parameters=CallbackServerParameters()) 
#gateway = ClientServer(java_parameters=JavaParameters(), python_parameters=PythonParameters()) 

java_import(gateway.jvm, 'javabridge.test.*') 

class PythonCallbackImpl(object): 
    def __init__(self, execfunc): 
     self.execfunc = execfunc 
    def notify(self, obj): 
     print('[PythonCallbackImpl] notified from Java') 
     self.execfunc() 
     return 'dummy return value' 
    class Java: 
     implements = ["javabridge.test.PythonCallback"] 

def simple_fun(): 
    print('[simple_fun] called') 
    gateway.jvm.System.out.println("[simple_fun] Hello from python!") 

# Test 1: Without threading 
input('Ready to begin test 1') 
python_callback = PythonCallbackImpl(simple_fun) 
nothread_executor = gateway.jvm.Test(python_callback) 
nothread_executor.runSynchronous() 

# Test 2: With threading 
input('Ready to begin test 2') 
python_callback = PythonCallbackImpl(simple_fun) 
nothread_executor = gateway.jvm.Test(python_callback) 
nothread_executor.runAsynchronous() 

gateway.shutdown() 

Folgendes passiert, wenn Sie versuchen, dieses Skript auszuführen. Zuerst mit gateway = ClientServer(java_parameters=JavaParameters(), python_parameters=PythonParameters()), scheitern beiden Tests:

Test 1: 

py4j.protocol.Py4JJavaError: An error occurred while calling o0.runSynchronous. 
: py4j.Py4JException: Command Part is Empty or is the End of Command Part 
     at py4j.Protocol.getObject(Protocol.java:277) 
     at py4j.Protocol.getReturnValue(Protocol.java:458) 

Test 2: 

Exception in thread "Thread-4" py4j.Py4JException: Error while obtaining a new communication channel 
     at py4j.CallbackClient.getConnectionLock(CallbackClient.java:218) 
     at py4j.CallbackClient.sendCommand(CallbackClient.java:337) 
     at py4j.CallbackClient.sendCommand(CallbackClient.java:316) 

Allerdings, wenn ich die self.execfunc() Zeilen aus kommentieren, Test 1 funktioniert ohne Fehler zu erhöhen. jedoch Test 2 immer noch nicht:

Exception in thread "Thread-5" py4j.Py4JException: Error while sending a command. 
     at py4j.CallbackClient.sendCommand(CallbackClient.java:357) 
     at py4j.CallbackClient.sendCommand(CallbackClient.java:316) 

Jetzt gateway = JavaGateway(callback_server_parameters=CallbackServerParameters()) wechseln. Als ich self.execfunc() halten Kommentar gesetzt, Test 2 immer noch nicht hier:

Exception in thread "Thread-5" py4j.Py4JException: Error while sending a command. 
     at py4j.CallbackClient.sendCommand(CallbackClient.java:357) 
     at py4j.CallbackClient.sendCommand(CallbackClient.java:316) 

Aber zumindest Test 1 funktioniert mit self.execfunc() aktiviert.

Meine Frage ist: Wie kann ich den Gewindeansatz mit dem self.execfunc() Aufruf verwenden? Ist das mit Py4J möglich?

Edit: und um die Dinge noch komplizierter zu machen, sollten Java-Befehle, die von self.execfunc() aufgerufen werden, im selben Java-Thread ausgeführt werden, der .notify() aufgerufen hat.

Antwort

2

Gelöst. Stellt sich als sehr einfach heraus:

  1. Verwenden Sie ClientServer auf der Python-Seite und auf der Java-Seite auch!
  2. Rufen gateway.shutdown nicht(), da dies Python trennen, bevor der Rückruf (duh!)

Java wird dann haften ordentlich auf den erwarteten Thread-Modell empfangen werden kann, dh Java durch die gerufene Kommandos Empfangen von Python-Callbacks werden in demselben Java-Thread ausgeführt, der den Callback ausgeführt hat.

Durch eine einfache Python-Funktion kann eine -Methode hinzugefügt werden, die wartet, bis alle Rückrufe vor dem Beenden zurückgekommen sind.