Я работаю над приложением, в котором есть компоненты, написанные на нескольких языках. Я пытаюсь получить функциональность, которая отлично работает в Java, работая в Jython. Существует некоторая встроенная функциональность / C ++, к которой Java обращается через JNI и упакована SWIG.
Всякий раз, когда я пытаюсь импортировать все классы в проекте, я получаю ошибки, которые PROJECTJNI не могут быть связаны. Вот мой минимальный случай для производства:
import sys
sys.path.append('PROJECT.jar')
from com.whatever.project import *
Вот сообщение об ошибке, когда это выполняется:
$ jython Bootstrap.py
"my" variable $jythonHome masks earlier declaration in same scope at /usr/bin/jython line 15.
Traceback (most recent call last):
File "Bootstrap.py", line 9, in <module>
from com.whatever.project import *
java.lang.UnsatisfiedLinkError: com.whatever.project.PROJECTJNI.swig_module_init()V
at com.whatever.project.PROJECTJNI.swig_module_init(Native Method)
at com.whatever.project.PROJECTJNI.<clinit>(PROJECTJNI.java:974)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:278)
at org.python.core.Py.loadAndInitClass(Py.java:909)
at org.python.core.Py.findClassInternal(Py.java:844)
at org.python.core.Py.findClass(Py.java:869)
at org.python.core.packagecache.PackageManager.basicDoDir(PackageManager.java:107)
at org.python.core.packagecache.SysPackageManager.doDir(SysPackageManager.java:138)
at org.python.core.PyJavaPackage.fillDir(PyJavaPackage.java:123)
at org.python.core.imp.importAll(imp.java:1051)
at org.python.core.imp.importAll(imp.java:1039)
at org.python.pycode._pyx0.f$0(Bootstrap.py:9)
at org.python.pycode._pyx0.call_function(Bootstrap.py)
at org.python.core.PyTableCode.call(PyTableCode.java:165)
at org.python.core.PyCode.call(PyCode.java:18)
at org.python.core.Py.runCode(Py.java:1275)
at org.python.util.PythonInterpreter.execfile(PythonInterpreter.java:235)
at org.python.util.jython.run(jython.java:247)
at org.python.util.jython.main(jython.java:129)
java.lang.UnsatisfiedLinkError: java.lang.UnsatisfiedLinkError: com.whatever.project.PROJECTJNI.swig_module_init()V
В строке 15 появляется каждый раз, когда мы вызываем jython, мы его игнорировали.
Мы можем заставить классы из Java Project работать, просто загружая их по одному:
com.whatever.project import Class1
com.whatever.project import Class2
...
com.whatever.project import Class50
Это очень непрактично, потому что даже для короткого скрипта на python нам может понадобиться дюжина классов. Многие из них являются исключениями, которые мы ловим, которые имеют уникальные типы. Таким образом, все, что надежно обрабатывает ошибки, может потребовать огромного количества классов.
В соответствии с документация по Jython Я должен быть в состоянии скрыть PROJECTJNI, чтобы он не загружался, делая что-то вроде этого, но я нашел документы менее чем совершенно ясно. Вот что я пытался:
import com.whatever.project
__all__ = dir(com.whatever.project)
__all__.remove('PROJECTJNI')
from com.whatever.project import *
Но это не удается с ошибками и все еще явно пытается загрузить PROJECTJNI.
Я также попытался исправить собственный исполняемый файл, чтобы он мог быть связан с корректным. Я узнал, что у другой группы, использующей JRuby, не было никаких проблем, включая все, поэтому я решил проверить исходный код и двоичный файл. Я нашел void swig_module_init () в файле Project_wrap.cpp, который создал Swig. Он был спрятан за макросом, но он был там, и objdump подтверждает:
$objdump libPROJECT.so -t |grep PROJECTJNI |grep init
000000000051a900 l O .data 00000000000009d0 _ZZ59Java_com_whatever_project_PROJECTJNI_swig_1module_1initE7methods
0000000000263f66 g F .text 00000000000000d1 Java_com_whatever_project_PROJECTJNI_swig_1module_1init
Я делаю что-то не так с любым из моих шагов по устранению неполадок? Это ошибка в Jython? Есть ли простой обходной путь Python, чтобы пропустить загрузку PROJECTJNI?
Все, что позволяет мне пропустить ссылку или сделать эту ссылку правильно, будет принято.
В одном из классов Java, BinaryLoader, был метод void load_binary (), который вызывается в статической секции этого класса:
public class BinaryLoader
{
static
{ load_binaries(); }
public static void load_binaries()
{
// Deep inside here is a call to actually load the shared library. Using
load_shared_library_from_jar("PROJECT");
}
... // more details here
}
Наш Java-код, использующий нативный API, вызвал этот метод в качестве загруженного класса. Наш код JRuby вызывал это, когда к классу «прикасались», просто используя имя константы, имя класса, вызывая статический раздел. Для пояснения здесь приведен пример из наших документов и реальных сценариев Ruby, который делает это:
# Tells Jruby to load the Java Interopability layer.
require 'java'
# Loads the PROJECT Java tools.
require 'PROJECT.jar'
# Shorten the PROJECT tool names from their obnoxiously long Java name.
module PROJECT
include_package "com.whatever.project"end
# Let Ruby know the BinaryLoader exists and it will have PROJECT load all the system binaries.
PROJECT::BinaryLoader
Очевидно, что JarFile достаточно, чтобы все нативные символы и классы Java были доступны в Ruby, и двоичный файл не загружается до последней строки, которая является просто выражением, которое разрешает имя класса и вынуждает статический раздел работать. Фактический вызов метода выглядел бы так: PROJECT :: BinaryLoader.load_binaries
В Jython статический раздел никогда не вызывался даже при вызове других статических методов в классе BinaryLoader. Я заставил его вызвать метод вручную, импортировав минимум Java и нативных символов для вызова метода:
# Tells Jython to load the python system Interopability tools.
import sys
# When searching for python symbols, this Java jar should be search also.
sys.path.append('PROJECT.jar')
# Load just enough the have the JVM Know how to load the native PROJECT libraries
from com.whatever.project import PROJECT
from com.whatever.project import BinaryLoader
# Load any native binaries that are required
BinaryLoader.load_binaries()
# After this line is run in any given script then PROJECT can be used.
from com.whatever.project import *
Очевидно, что родные двоичные файлы были просто не загружены, как предполагалось. Все, что загрузило их, было бы приемлемым решением.
Это работает вокруг Jython, не вызывающего статический раздел, выполняя работу, которую он должен выполнить как можно раньше. Это похоже на ошибку в Jython, но может случиться так, что гарантии на статический порядок загрузки не строго соблюдаются в Java. В любом случае, из-за этой несовместимости мы, вероятно, удалим статический раздел, чтобы предотвратить добавление чем-то будущим Java-рекламы Ruby Devs чего-либо, что может не загрузиться в Python.
Других решений пока нет …