Re: [Py4j-users] An issue in reflection?
Status: Beta
Brought to you by:
barthe
From: Jeremy <ken...@gm...> - 2014-05-18 03:01:18
|
Hah, turns out it was the missing 'self.' changed it to self.__eq__(obj) and it works! (Getting my Java and Python mixed up.) Its great that I can just pass around the interface now. There's one last thing that has been confusing me and that is launching the server using the java_gateway.launch_gateway(). I've been trying to start the app using that instead of manually calling the popen() myself. My directory layout looks like this: root | | -Launcher.py | -MainWindow.py | -Other stuff .py | lib \ | -common-codec.jar | -jackson-core.jar | -py4j-0.8.1.jar | -pctelelog-gateway-server.jar | -Other dependencies for gateway server Looking at the documentation for launch_gateway() I decided to try using this in my Launcher. self.__gateway = JavaGateway.launch_gateway(jarpath="./lib/pctelelog-gateway-server.jar", classpath="pctelelog.TeleLogPy4jLauncher", die_on_exit=True) TeleLogPy4jLauncher.java (inside the pctelelog-gateway-server.jar) package pctelelog; /*Imports*/ public class TeleLogPy4jLauncher implements GatewayServerListener { private static Logger logger = LogManager.getLogger(TeleLogPy4jLauncher.class); private static TeleLogServer m_telelogServer = new TeleLogServer(); public TeleLogPy4jLauncher() { logger.info("Logging started."); m_telelogServer.start(); } public void addEventListener(EventListener listener) { logger.info("Listener added."); m_telelogServer.addEventListener(listener); } public void removeEventListener(EventListener listener) { logger.info("Listener removed."); m_telelogServer.removeEventListener(listener); } public static void main(String[] args) { TeleLogPy4jLauncher launcher = new TeleLogPy4jLauncher(); GatewayServer gateway = new GatewayServer(launcher); gateway.addListener(launcher); gateway.start(); } /* GatewayListener implementations */ } When I run Launcher.py I get a StringIndexOutofBoundsException when the addEventListener is called in my MainWindow initCallback(). If I launch the JAR manually everything works fine. So I feel like there must be something I'm misunderstanding about the way to use launch_gateway. Maybe it won't work with my server inside a jar? Thanks for the help and patience so far! Jeremy On 5/17/14, 5:17 PM, Barthelemy Dagenais wrote: > Hi, > > regarding the equals method, I believe you were in the right direction: > > def equals(self, obj): > # obj will be a JavaObject type, so obj is never equal to self > return obj is not None and obj.toString() == self.toString() > > def toString(self): > return "Something Unique" > > In one of your examples, you call __eq__(obj), but it should be self.__eq__(obj) > > A good practice when you are not sure what is going on is to always > wrap your code in: > > try: > # unsafe code > except Exception: > from traceback import print_exc > print_exc() > > Sometimes, exceptions are swallowed or shadowed by other exceptions > and this is a trick that saved me a lot of time in the past ;-) > > Finally, I'm not sure why you need the setter (vs the constructor with > the parameter). I'll need to investigate and I'll try to get back to > you this week. > > Thanks, > Bart > > > On Fri, May 16, 2014 at 7:35 PM, Jeremy <ken...@gm...> wrote: >> Sorry for the delayed reply. Been busy, finally got time to work on my >> project. >> >> I looked at the Vector.java code and can see that it calls the Object >> class method, equals(). >> So taking your suggestion, I added an equals(Object) method to the >> interface (not necessary likely) and tried to implement something in the >> implementing class in Python. Something like: >> >> class equals(self, obj): >> if self == obj: >> return True >> return False >> >> But still got the same error. Tried adding in Python's global equal, and >> that didn't work either. And even tried creating a UUID for each >> EventListener, but that didn't work either. It just can't seem to find >> the equals method. My final EventListener ended up looking like this. >> >> class EventListener(object): >> id = uuid.uuid4() >> >> def onEvent(self, event): >> pass >> >> def equals(self, obj): >> return __eq__(obj) >> >> def __eq__(self, obj): >> if obj == None: >> return False >> >> if obj.id == self.id: >> return True >> else: >> return False >> >> class Java: >> implements = ['pctelelog.EventListener'] >> >> pass >> >> >> My solution that seems to be working now has been to remove passing >> around an interface and instead rename the interface EventCallback and >> wrap it inside a new class (EventListener) and pass an object of that >> class around. It works fine but I have a strange issue now, maybe I'm >> doing it wrong. >> >> Here's my new EventListener.java >> public class EventListener implements EventCallback { >> >> private EventCallback m_callback = null; >> >> public EventListener() { >> } >> >> public EventListener(EventCallback callback) { >> assert callback != null; >> >> m_callback = callback; >> } >> >> public void setCallback(EventCallback callback) { >> m_callback = callback; >> } >> >> public static EventListener createEventListener(EventCallback >> callback) { >> return new EventListener(callback); >> } >> >> @Override >> public void onEvent(AbstractEvent event) { >> m_callback.onEvent(event); >> } >> } >> >> Here's the Python I use to get an instance of the class and then add it >> to the listener pool: >> (This method sits inside a PyQt Widget class.) >> >> def initCallback(self, func): >> callback = EventCallback() >> callback.onEvent = self.onEvent >> self.eventListener = >> self.__gateway.jvm.pctelelog.EventListener(callback) >> self.__gateway.entry_point.addEventListener(self.eventListener) >> >> >> When I try to do it like that I get an error (Logger + Error): >> >> Callback Server Starting >> Socket listening on ('127.0.0.1', 25334) >> Command to send: r >> u >> pctelelog >> rj >> e >> >> Answer received: yp >> Command to send: r >> u >> pctelelog.EventListener >> rj >> e >> >> Answer received: ycpctelelog.EventListener >> Traceback (most recent call last): >> File >> "/Users/Shared/eclipse/workspace/PCTeleLog-PyQt/pytelelog-pyqt/Launcher.py", >> line 27, in <module> >> Launcher() >> File >> "/Users/Shared/eclipse/workspace/PCTeleLog-PyQt/pytelelog-pyqt/Launcher.py", >> line 23, in __init__ >> mainWindow = MainWindow(self.__gateway) >> File >> "/Users/Shared/eclipse/workspace/PCTeleLog-PyQt/pytelelog-pyqt/MainWindow.py", >> line 25, in __init__ >> self.initCallback(self.onEvent) >> File >> "/Users/Shared/eclipse/workspace/PCTeleLog-PyQt/pytelelog-pyqt/MainWindow.py", >> line 49, in initCallback >> self.eventListener = >> self.__gateway.jvm.pctelelog.EventListener(callback) >> File "/usr/local/lib/python2.7/site-packages/py4j/java_gateway.py", >> line 662, in __call__ >> args_command = ''.join([get_command_part(arg) for arg in args]) >> File "/usr/local/lib/python2.7/site-packages/py4j/protocol.py", line >> 261, in get_command_part >> command_part = PYTHON_PROXY_TYPE + python_proxy_pool.put(parameter) >> AttributeError: 'NoneType' object has no attribute 'put' >> >> === >> Now, if I change it around and instantiate with the void constructor and >> set the callback with the setter. It works fine. >> >> def initCallback(self, func): >> callback = EventCallback() >> callback.onEvent = self.onEvent >> self.eventListener = self.__gateway.jvm.pctelelog.EventListener() >> self.eventListener.setCallback(callback) >> self.__gateway.entry_point.addEventListener(self.eventListener) >> >> I'd rather do without the setter if there was a way to manage it. Any >> idea what's going on? >> >> >> Thanks! >> >> On 5/7/14, 7:54 AM, Barthelemy Dagenais wrote: >>> Hi! >>> >>> Thanks for using Py4J! I'll try to look at this issue more deeply >>> during the weekend, but from the stack trace, it seems the Java side >>> is sending a method call to the Python side and the Python side is >>> replying with an exception: >>> >>> Error: >>> py4j.protocol.Py4JJavaError: An error occurred while calling >>> t.removeEventListener. >>> : py4j.Py4JException: An exception was raised by the Python Proxy. >>> Return Message: x >>> >>> Also, from the stack trace, it appears that when deleting the listener >>> from the Java vector, the vector calls the "equals" method on the >>> listener. This is the first time I have to deal with this scenario and >>> I believe the equals method is delegated to the python side. Because >>> it is not implemented, I believe this may be the cause of failure. A >>> workaround might be to try to implement an "equals" method in your >>> Python listener. >>> >>> Do you have logging enabled on the Python side? Any stack trace visible there? >>> >>> Btw, you should subscribe to the mailing list, it would be easier to >>> manage the replies :-) >>> >>> Bart >>> >>> On Tue, May 6, 2014 at 7:58 PM, Jeremy <ken...@gm...> wrote: >>>> So I'm trying to use Py4j to let a UI talk to a Java server. The java >>>> server uses 3 threads (entry-point, server socket, client-event >>>> dispatch). The python UI registers an event call back listener and that >>>> listener eventually gets dumped into a Vector. I had some initial >>>> trouble getting the addEventListener() working but fixed it (somehow). >>>> But now any calls to removeEventListener() don't work. >>>> >>>> My first guess was something to do with different instances of the >>>> object when Vector does the lookup, but I'm not sure. I don't understand >>>> relfection concepts well enough and the Py->Java (callback) thing seems >>>> to suggest I should be fine since the original eventlistener hasn't been >>>> garbage collected yet. If you have any insight that would be great. >>>> >>>> Error: >>>> py4j.protocol.Py4JJavaError: An error occurred while calling >>>> t.removeEventListener. >>>> : py4j.Py4JException: An exception was raised by the Python Proxy. >>>> Return Message: x >>>> at py4j.Protocol.getReturnValue(Protocol.java:417) >>>> at >>>> py4j.reflection.PythonProxyHandler.invoke(PythonProxyHandler.java:113) >>>> at com.sun.proxy.$Proxy0.equals(Unknown Source) >>>> at java.util.Vector.indexOf(Vector.java:404) >>>> at java.util.Vector.indexOf(Vector.java:378) >>>> at java.util.Vector.removeElement(Vector.java:637) >>>> at java.util.Vector.remove(Vector.java:795) >>>> at >>>> pctelelog.EventOperatorThread.removeEventListener(EventOperatorThread.java:20) >>>> at pctelelog.ServerThread.removeEventListener(ServerThread.java:16) >>>> at pctelelog.Launcher.removeEventListener(Launcher.java:17) >>>> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) >>>> at >>>> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) >>>> >>>> This code is a stripped down version of the development version but both >>>> are generating the same error. >>>> >>>> package pctelelog; >>>> >>>> public class Event { >>>> public String testData = "test"; >>>> } >>>> >>>> === >>>> >>>> package pctelelog; >>>> >>>> public interface EventListener { >>>> public void onEvent(Event event); >>>> } >>>> >>>> === >>>> >>>> package pctelelog; >>>> >>>> import java.util.Vector; >>>> >>>> public class EventOperatorThread extends Thread { >>>> >>>> private Vector<EventListener> m_listeners = new >>>> Vector<EventListener>(); >>>> >>>> public void run() { >>>> while(true) {} >>>> } >>>> >>>> public synchronized void addEventListener(EventListener listener) { >>>> System.out.println("ADD"); >>>> m_listeners.add(listener); >>>> } >>>> >>>> public synchronized void removeEventListener(EventListener listener) { >>>> System.out.println("REMOVE"); >>>> m_listeners.remove(listener); >>>> } >>>> } >>>> >>>> === >>>> >>>> package pctelelog; >>>> >>>> public class ServerThread extends Thread { >>>> >>>> private EventOperatorThread m_operator = new EventOperatorThread(); >>>> >>>> public void run() { >>>> m_operator.start(); >>>> while(true) {} >>>> } >>>> >>>> public synchronized void addEventListener(EventListener listener) { >>>> m_operator.addEventListener(listener); >>>> } >>>> public synchronized void removeEventListener(EventListener listener) { >>>> m_operator.removeEventListener(listener); >>>> } >>>> >>>> } >>>> >>>> === >>>> >>>> package pctelelog; >>>> >>>> import py4j.GatewayServer; >>>> >>>> public class Launcher { >>>> private ServerThread m_server = new ServerThread(); >>>> >>>> public Launcher() { >>>> m_server.start(); >>>> System.out.println("Server started."); >>>> } >>>> >>>> public void addEventListener(EventListener listener) { >>>> m_server.addEventListener(listener); >>>> } >>>> public void removeEventListener(EventListener listener) { >>>> m_server.removeEventListener(listener); >>>> } >>>> >>>> public static void main(String[] args) { >>>> GatewayServer gateway = new GatewayServer(new Launcher()); >>>> gateway.start(); >>>> } >>>> } >>>> >>>> === >>>> >>>> from py4j.java_gateway import JavaGateway, GatewayClient >>>> >>>> class EventListener(object): >>>> >>>> def onEvent(self, event): >>>> print event.testData; >>>> >>>> class Java: >>>> implements = ['pctelelog.EventListener'] >>>> >>>> class Launcher: >>>> >>>> def __init__(self): >>>> self.__gateway = JavaGateway(start_callback_server=True) >>>> listener = EventListener() >>>> self.__gateway.entry_point.addEventListener(listener) >>>> self.__gateway.entry_point.removeEventListener(listener) >>>> >>>> if __name__ == '__main__': >>>> Launcher() >>>> pass >>>> >>>> >>>> ------------------------------------------------------------------------------ >>>> Is your legacy SCM system holding you back? Join Perforce May 7 to find out: >>>> • 3 signs your SCM is hindering your productivity >>>> • Requirements for releasing software faster >>>> • Expert tips and advice for migrating your SCM now >>>> http://p.sf.net/sfu/perforce >>>> _______________________________________________ >>>> Py4j-users mailing list >>>> Py4...@li... >>>> https://lists.sourceforge.net/lists/listinfo/py4j-users >> >> ------------------------------------------------------------------------------ >> "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE >> Instantly run your Selenium tests across 300+ browser/OS combos. >> Get unparalleled scalability from the best Selenium testing platform available >> Simple to use. Nothing to install. Get started now for free." >> http://p.sf.net/sfu/SauceLabs >> _______________________________________________ >> Py4j-users mailing list >> Py4...@li... >> https://lists.sourceforge.net/lists/listinfo/py4j-users > ------------------------------------------------------------------------------ > "Accelerate Dev Cycles with Automated Cross-Browser Testing - For FREE > Instantly run your Selenium tests across 300+ browser/OS combos. > Get unparalleled scalability from the best Selenium testing platform available > Simple to use. Nothing to install. Get started now for free." > http://p.sf.net/sfu/SauceLabs > _______________________________________________ > Py4j-users mailing list > Py4...@li... > https://lists.sourceforge.net/lists/listinfo/py4j-users |