Почему этот цикл Jython выходит из строя после одного прогона?

У меня есть следующий код:

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();
    }

Для полноты я добавляю код Python:

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()

Он работает безупречно, но только в 10% случаев. Если я запустил его следующим образом:

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

Он работает в первый раз. Он дает мне вывод, который я хочу, который является данными из HTTP-запроса, который написан в моем файле Versions.py, который имеет код java выше вызовов. После второго раза он выдает эту массивную ошибку (длина которой составляет 950 строк, но, конечно, я не буду мучить вас, ребята). Вот его суть:

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, которая поставляется в конце трассировки стека строк 950, в основном такова:

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

Если кому-то интересно, кажущаяся оскорбительная строка в моем get_latest_version справедлива:

handler = urllib2.urlopen(request)

Поскольку сервер, на котором выполняется вызов кода, запускается (через cherrypy) на localhost в моей сети, я вижу, как он взаимодействует с моим сервером. Он фактически отправляет два запроса (и выбрасывает исключение сразу после второго).

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"

В то время как я никогда не буду запускать этот код в цикле, вероятно, мне очень интересно узнать о двух вещах:

  • Является ли код нарушения моим кодом Python или Java? Или это может быть просто проблема с Jython?
  • Что означает исключение (похоже, это исключение Java)? Почему его бросают, когда это происходит? Есть ли способ сделать цикл, подобный этой работе? Может ли это быть написано лучше?

Ответы

Ответ 1

В библиотеке python urllib2, которую вы используете, используется Netty.

Netty имеет проблему, которая широко известна:

По всем этим ссылкам Netty HttpClient время от времени выходит из строя после закрытия. Похоже, что Netty восстанавливается через некоторое время, и некоторые приложения работают нормально с этой проблемой. В любом случае, он выглядит неустойчивым.


Q: Является ли код нарушения моим кодом Python или Java? Или это может быть просто проблема с Jython?

A: Проблема вызвана библиотекой Jython urllib2, которая использует Netty.


Q: Что означает исключение (похоже, это исключение Java)? Почему он бросается, когда он есть?

A: urllib2 использует внутренне Netty. Netty написан на Java и выбрасывает это исключение Java. Netty использует собственный Thread Executor, который отключается и неприменим в течение некоторого времени после закрытия запроса. Вы набрали именно это время.


Q: Есть ли способ сделать цикл таким образом? Может ли это быть написано лучше?

A: Я попытался бы использовать библиотеку Requests.

Ответ 2

Попробуйте дать интерпретатору свежее инициализированное состояние системы каждый раз, когда вы его создаете:

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