15

I've got the following code:

public static String getVersion() 
{
    PythonInterpreter interpreter = new PythonInterpreter();

    try 
    {
        interpreter.exec(IOUtils.toString(new FileReader("./Application Documents/Scripts/Version.py")));
        PyObject get_version = interpreter.get("get_latest_version");
        PyObject result = get_version.__call__(interpreter.get("url"));
        String latestVersion = (String) result.__tojava__(String.class);
        interpreter.close();
        return latestVersion;
    } catch (IOException ex) {
        ex.printStackTrace();
        interpreter.close();
        return Version.getLatestVersionOnSystem();
    }

For the sake of completeness, I'm adding the Python code:

import urllib2 as urllib
import warnings

url = 'arcticlights.ca/api/paint&requests?=version'

def get_latest_version(link=url):
    request = urllib.Request(link)
    handler = urllib.urllopen(request)
    if handler.code is not 200:
        warnings.warn('Invalid Status Code', RuntimeWarning)
    return handler.read()

version = get_latest_version()

It works flawlessly, but only 10% of the time. If I run it with a main like follows:

public static void main(String[] args)
{
    for (int i = 0; i < 10; i++) {
        System.out.println(getVersion());
    }   
}

It works the first time. It gives me the output that I want, which is the data from the http request that is written in my Versions.py file, which the java code above calls. After the second time, it throws this massive error (which is 950 lines long, but of course, I won't torture you guys). Here's the gist of it:

Aug 26, 2015 10:41:21 PM org.python.netty.util.concurrent.DefaultPromise execute
SEVERE: Failed to submit a listener notification task. Event loop shut down?
java.util.concurrent.RejectedExecutionException: event executor terminated

My Python traceback that is supplied at the end of the 950 line Java stack trace is mostly this:

File "<string>", line 18, in get_latest_version 
urllib2.URLError: <urlopen error [Errno -1] Unmapped exception: java.util.concurrent.RejectedExecutionException: event executor terminated>

If anyone is curious, the seemingly offending line in my get_latest_version is just:

handler = urllib2.urlopen(request)

Since the server that the code is calling is being run (by cherrypy) on the localhost on my network, I can see how it is interacting with my server. It actually sends two requests (and throws the exception right after the second).

127.0.0.1 - - [26/Aug/2015:22:41:21] "GET / HTTP/1.1" 200 3 "" "Python-urllib/2.7"
127.0.0.1 - - [26/Aug/2015:22:41:21] "GET / HTTP/1.1" 200 3 "" "Python-urllib/2.7"

While I'm never going to run this code in a loop likely, I'm quite curious as to two things:

  • Is the offending code my Python or Java code? Or could it just be an issue with Jython altogether?
  • What does the exception mean (it looks like a java exception)? Why is it being thrown when it is? Is there a way to make a loop like this work? Could this be written better?
Zizouz212
  • 4,908
  • 5
  • 42
  • 66
  • It's possible that you're accessing the resource too quickly, that in the first call to `getVersion()` you got a lock on the file that hasn't been released yet. Can you try calling [Thread.sleep](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#sleep%28long%29) or [Thread.yield](https://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#yield%28%29) after `getVersion()` in the loop? – DaedalusUsedPerl Aug 31 '15 at 05:15
  • @DaedalusUsedPerl I'll try sleeping the thread, how long do you think I should do that for? 2-3 seconds perhaps? – Zizouz212 Aug 31 '15 at 16:03
  • Even a second might be overkill, but I'm not sure. Try 2 and adjust up/down to fit. – DaedalusUsedPerl Aug 31 '15 at 16:06
  • Does it work 5% of the time if the loop is run 20 times? This looks like an issue with resource cleanup by the interpreter. Try having the interpreter set up as a static field instead and not calling close afterwards. – Matjaž Pečan Sep 01 '15 at 08:14
  • @DaedalusUsedPerl It doesn't really do anything, same result. Tested even up to 5 seconds. – Zizouz212 Sep 02 '15 at 23:41
  • @MatjažPečan I'll try that. I'll update you on how it goes. My biggest wonder is that the resource should be closed by the time the next one goes. And since they are local variables to a method, and not an instance, it feels weird. The resource should have closed by the time the next one started. Such is confusion :P – Zizouz212 Sep 02 '15 at 23:42
  • Looking at the code for PythonInterpreter I would expect it to still work after being closed and re-initialized, as they use ThreadLocals to store the state and clean-up properly in close (from what I can tell at a cursory glance). There could be some state left over that I did not detect and that would probably be the reason for your issues. – Matjaž Pečan Sep 03 '15 at 07:35
  • @MatjažPečan Do you think I should start it on a separate thread (each interpreter separately)? – Zizouz212 Sep 03 '15 at 19:12
  • Can you simplify python script so that it just returns a value? If it will work then the problem is in python. If not - in PythonInterpreter. – Oleg Rudenko Sep 03 '15 at 19:32
  • Could you run post at least some of the 950 lines long error? I hope the lines are call stack, that may help diagnose the problem –  Sep 03 '15 at 21:00
  • @Arkadiy I would, but for some reason it is all completely repetitive. I'll post it though. – Zizouz212 Sep 03 '15 at 21:04

2 Answers2

8

The python library urllib2, which you use, uses Netty.

Netty has a problem, which is widely known:

According to all of these links Netty HttpClient fails from time to time after closing. It looks like Netty recovers after some time and some applications work just normally with this problem. Anyway it looks unstable.


Q: Is the offending code my Python or Java code? Or could it just be an issue with Jython altogether?

A: The problem is caused by Jython library urllib2, which uses Netty.


Q: What does the exception mean (it looks like a java exception)? Why is it being thrown when it is?

A: urllib2 uses internally Netty. Netty is written in Java and throws this Java exception. Netty uses its own Thread Executor, which is shut down and unusable for some time after closing a request. You hit exactly this time.


Q: Is there a way to make a loop like this work? Could this be written better?

A: I would try to use Requests library.

Community
  • 1
  • 1
Oleg Rudenko
  • 708
  • 4
  • 12
  • This doesn't answer my question *at* all. I'm looking for an explanation to the exception, and why it doesn't work. This doesn't do anything to help me. What throws the exception? Why? Is it a bug with the implementation? Interestingly, it looks like you haven't bothered reading the question: I have a single Jython environment at a time, not multiple at the same time. – Zizouz212 Sep 03 '15 at 19:06
  • @Zizouz212 It was not easy to analyze the problem completely without your python code. Still it looks like I have found the cause. *I have completely rewritten the answer.* Hope, it will help now. – Oleg Rudenko Sep 03 '15 at 20:13
  • Thanks @OlegRudenko. Interesting to read the issue cause and nice tracing it to Netty. I was going to look at it next, but ran out of time. – Matjaž Pečan Sep 04 '15 at 10:49
  • @OlegRudenko This is much better. I normally use requests for all my python work, but how do I get this to work using Jython? I've never managed to get it, even with manipulating the `sys.path` and working directories. Otherwise, this answer is much, much, much better :D – Zizouz212 Sep 04 '15 at 11:17
  • @Zizouz212 Sorry, I don't have experience with this problem. Maybe this Answer will be helpful: [How to get urllib3 and requests working with jython 2.7 beta 1?](http://stackoverflow.com/questions/14919557/how-to-get-urllib3-and-requests-working-with-jython-2-7-beta-1) – Oleg Rudenko Sep 04 '15 at 14:50
  • One more question. Is Netty a java library? – Zizouz212 Sep 04 '15 at 23:57
0

Try giving the interpreter a freshly initialized system state each time you create it:

PySystemState.initialize();
PythonInterpreter interpreter = new PythonInterpreter(null, new PySystemState());
Guy
  • 1