Интеграция Python 2.3 и Java Native Interface в AIX 5L

Как решить проблему, связанную с совместимостью Python 2.3 при работе с Java™ Native Interface (JNI) на AIX5L™? Нам Кеунг и Роджер Леки рассказывают, как исправить ошибку при компоновке программы на Python 2.3 с JNI-программой, используя примеры кода для интеграции Python 2.3 с JNI-вызовами.

Нам Кеунг, Старший программист, IBM

Нам Кеунг (Nam Keung) -- старший программист, работавший в области развития AIX коммуникаций, AIX multimedia, разработки SOM/DSOM и производительности java. В настоящее время его назначение -- помощь ISV в разработке приложений, внедрение приложений, настройка производительности и обучение платформе IBM pSeries. Он работает программистом в IBM с 1989. Связаться с ним можно по адресу namkeung@us.ibm.com.



Роджер Лейаки, экс-руководитель команды ISV and UNIX Technical Support for Java, IBM

Роджер Лейаки – руководитель команды ISV (разработчиков независимого ПО) и команды технической поддержки Java для UNIX (Остин, Техас). С ним можно связаться с ним по адресу "*rog@us.ibm.com.



15.01.2010

Введение

Дистрибутив AIX 5L содержит уже скомпилированную версию библиотек Python 2.2, которые можно интегрировать с JNI, позволив тем самым программам на Java и Python работать в рамках одного процесса. Однако, если загрузить открытые исходные коды Python с его Web-сайта и скомпилировать их при помощи прилагаемых файлов makefile, то интеграция Java и Python может привести к непредсказуемым результатам. Информация, содержащаяся в этой статье, относится для Python 2.3.


Подробности

В основном, проблемы при интеграции Java и Python создает компоновка на этапе выполнения. Из-за способа, которым в AIX создаются исполняемые файлы платформы Java и опций по умолчанию компоновщика, используемого в make-процессе Python, эти две технологии становятся несовместимыми при совместной компоновке. Версия Python (2.2), предоставляемая с дистрибутивом AIX, специально была собрана так, чтобы избежать описанной проблемы.

Существует следующий сценарий интеграции Python и Java:

  • создать Java-класс, который загружает и обращается к программе на Python (JNI);
  • интегрировать Java-класс в Java-приложение;
  • выполнить Java-программу.

Например:

MyApp.java	-> MyClass.java (JNI)	-> Python (C code)

Ниже представлены простые программы и makefile, которые демонстрируют поведение по умолчанию при использовании Python 2.2 и Python 2.3 с Java.

Листинг 1. SimpleTime.java
public class SimpleTime {
    public native void displayTime();
    static {
        System.loadLibrary("distime");      // загрузка общего объекта .so
    }
    public static void main(String []args) {
        new SimpleTime().displayTime();     // вызов функции simple.c
    }
}
Листинг 2. simple.c
#include <Python.h>
#include <stdio.h>
#include <dlfcn.h>
#include "SimpleTime.h"

JNIEXPORT void JNICALL
Java_SimpleTime_displayTime(JNIEnv *env, jobject obj)
{
  printf("----displayTime(1)\n");
  Py_Initialize();
  printf("----displayTime(2)\n");
  PyRun_SimpleString("import time");
  PyRun_SimpleString("print 'local time:',time.localtime(time.time())");
  Py_Finalize();
  return;
}
Листинг 3. Makefile
all:    SimpleTime.class SimpleTime.h libdistime.so

CC              = cc_r
JAVAH           = javah -jni
JAVAC           = javac

JAVA_INCLUDE    = -I${JAVA_HOME}/include
PYTHON_INCLUDE  = -I${PYTHON_INC_HOME}
INCLUDE_ALL     = $(JAVA_INCLUDE) $(PYTHON_INCLUDE)

MYLINE          = -L${PYTHON_LIB_HOME} -bE\:${PYTHON_LIB_HOME}/config/python.exp
 -lpython2.2 -bM:SRE -T512 -H512 -lc_r -lpthread -lm

SimpleTime.class:       SimpleTime.java
        $(JAVAC) SimpleTime.java

SimpleTime.h:   SimpleTime.class
        $(JAVAH) SimpleTime

simple.o:       simple.c
        $(CC) -g $(INCLUDE_ALL) $(MYLINE) -c simple.c -o simple.o

libdistime.so:  simple.o libdistime.exp
        -rm -f libdistime.so
        ld -bexpall -o libdistime.so -bnoentry $(MYLINE) -bE\:libdistime.exp  
             `cat libdistime.lst`

libdistime.exp: simple.o libdistime.lst
        ./mkexpfile libdistime.exp `cat libdistime.lst`

pythons.exp: simple.o
        ./mkexpfile pythons.exp 
             `find /usr/local/lib/python2.2/lib-dynload -name "*.so" -print `

libdistime.lst:
        find . -name "simple.o" > libdistime.lst

clean:
        /bin/rm -rf *.o *.class *.h *.so

Вывод данных при помощи Python 2.2 и 2.3

Листинг 4. Вывод данных в Python 2.2
>	java SimpleTime
>	----displayTime(1)
>	----displayTime(2)
>	local time: (2005, 1, 5, 12, 42, 8, 2, 5, 0)
Листинг 5. Вывод данных в Python 2.3
>	java SimpleTime
>	----displayTime(1)
>	----displayTime(2)
>	Traceback (most recent call last):
>	  File "<string>", line 1, in ?
>	ImportError: from module /opt/freeware/lib/python2.3/lib-
dynload/time.so Cannot run a file that does not have a valid format.
>	  /opt/freeware/lib/python2.3/lib-dynload/time.so
>	 PyArg_Parse 0 java
>	 PyArg_ParseTuple 1 java
>	 PyDict_GetItemString 2 java
>	 PyErr_NoMemory 3 java
>	 PyErr_Occurred 4 java
>	 PyErr_SetFromErrno 5 java
>	 PyErr_SetString 6 java
>	 PyEval_RestoreThread 7 java
>	 PyEval_SaveThread 8 java
>	 PyExc_IOError 9 java
>	 PyExc_OverflowError 10 java
>	 PyExc_ValueError 11 java
>	 PyFloat_FromDouble 12 java
>	 PyImport_ImportModule 13 java
>	 PyInt_AsLong 14 java
>	 PyInt_FromLong 15 java
>	 PyInt_Type 16 java
>	 PyModule_AddIntConstant 17 java
>	 PyModule_AddObject 18 java
>	 PyModule_GetDict 19 java
>	 PyObject_CallMethod 20 java
>	 PyString_FromString 21 java
>	 PyString_FromStringAndSize 22 java
>	 PyStructSequence_InitType 23 java
>	 PyStructSequence_New 24 java
>	 PyTuple_Size 25 java
>	 PyType_IsSubtype 26 java
>	 Py_BuildValue 27 java
>	 Py_IgnoreEnvironmentFlag 28 java
>	 Py_InitModule4 29 java
>	 _Py_NoneStruct 30 java
>	local time:
>	Traceback (most recent call last):
>	  File "<string>", line 1, in ?
>	NameError: name 'time' is not defined

Различие между Python 2.2 и Python 2.3

В Python 2.3 команда разработчиков Python модифицировала опции компоновки таким образом, чтобы они динамически выбирали и загружали нужные модули. До этого модули, установленные в каталоге /opt/freeware/lib/python-2.2/lib-dynload, использовали отложенные (deferred) или анонимные (anonymous) директивы импорта. Механизм загрузки должен был бы использовать системный вызов loadbind(), при помощи которого он мог бы сопоставить эти директивы импорта модулям в процессе, и определения этих директив были бы найдены в каком-нибудь месте. В программном коде для версии 2.2 видно, что все вновь загружаемые модули привязываются ко всем уже существующим модулям в процессе; такая всеобъемлющая техника позволяет получить необходимые идентификаторы из любого модуля.

В подходе, реализованном в Python 2.3, используется функция, в большей степени использующая AIX, которая позволяет модулям объявлять, что необходимые для них идентификаторы доступны в главном исполняемом файле (независимо от того, так это или не так); главный исполняемый файл теперь должен предоставить («экспортировать») модулям эти идентификаторы. Главный исполняемый файл на Python так и поступит, а вот среда Java сделать этого не сможет. Очевидно, что у этой методики более строгие требования к организации приложения, чем у методики, описанной выше.


Решение проблемы

Следующий подход предоставляет удобное решение описанной выше проблемы, возникающей при использовании Java с Python 2.3. Основа данной методики заключается в следующем: создать главную программу, которая моделирует исполняемый файл Java, затем скомпилировать и скомпоновать эту программу с использованием необходимых для поддержки Python опций.

Сначала необходимо загрузить, установить и собрать требуемую версию Python (в данном случае используется Python 2.3), после этого сохранить копию исходного кода в файлы javamain.c и java.make, показанные ниже, и внести необходимые изменения в makefile. Затем собрать и запустить тестовую программу.

Листинг 6. javamain
javamain jvm_options your_class  your_class_options
Листинг 7. javamain.c
#include <jni.h>
#define PATH_SEPARATOR ':'
#define USER_CLASSPATH "." /* где находится Prog.class */

int main( int argc, char *argv[]) {

    JNIEnv *env;
    JavaVM *jvm;
    jint res;
    jclass cls;
    jmethodID mid;
    jstring jstr;
    jobjectArray args;
    char classpath[1024];

    JavaVMInitArgs vm_args;
    JavaVMOption options[100];
    int i,j;

    /* Важно: при использовании JDK1.1.2 
    и более поздних версий указать vm_args version # */
    if ( argc < 2 ) {
        fprintf( stderr, "usage: %s java_opts java_class class_opts\n", argv[0]);
        exit( 1);
    }

    vm_args.nOptions = 0;
    for( i = 1; ( argv[ i][ 0] == '-'); i++) {
        vm_args.nOptions++;
        options[ i - 1].optionString = argv[ i];
    }

    if ( i == argc ) {
        fprintf(stderr, "usage: %s java_opts java_class class_opts\n", argv[0]);
        exit( 1);
    }

    vm_args.version = JNI_VERSION_1_2;
    vm_args.options = options;
    vm_args.ignoreUnrecognized = TRUE;

    JNI_GetDefaultJavaVMInitArgs(&vm_args);

    /* Присоединить значение переменной USER_CLASSPATH в конец значения
	переменной SYSTEM CLASSPATH (путь для поиска библиотек Java) */
    /* создать Java VM */
    res = JNI_CreateJavaVM(&jvm,&env,&vm_args);
    if (res < 0) {
        fprintf(stderr, "Can't create Java VM\n");
        exit(1);
    }

    cls = (*env)->FindClass(env, argv[i]);
    if (cls == 0) {
        fprintf(stderr, "Can't find %s class\n", argv[ i]);
        exit(1);
    }
    i++;
    
    mid = (*env)->GetStaticMethodID(env, cls, "main","([Ljava/lang/String;)V");
    if (mid == 0) {
     fprintf(stderr, "Can't find Prog.main\n");
     exit(1);
    }

    if (( argc - i ) > 0 ) {
          args = (*env)->NewObjectArray( env, ( argc - i),
                 (*env)->FindClass( env,"java/lang/String"),
                 (*env)->NewStringUTF( env, argv[i]));

          if (args == 0) {
                 fprintf(stderr, "Out of memory\n");
                 exit(1);
          }
          i++;

         for(; ( i <argc); i++) {
         (*env)->SetObjectArrayElement(env, args, i-2, (*env)->
              NewStringUTF(env, argv[i]));
         }
     } else {
        args = NULL;
     }

    (*env)->CallStaticVoidMethod(env, cls, mid, args);
    (*jvm)->DestroyJavaVM(jvm);
}
Листинг 8. java.make
all: javamain #Prog.class
JAVAHOME    = /usr/java14
JAVA_INC    = -I$(JAVAHOME)/include
JAVA_LIBS   = -L$(JAVAHOME)/jre/bin -L$(JAVAHOME)/jre/bin/classic -ljvm -ljava
JAVA_INCLUDE    = -I${JAVA_HOME}/include
PYTHON_INCLUDE  = -I${PYTHON_INC_HOME}
INCLUDE_ALL     = $(JAVA_INCLUDE) $(PYTHON_INCLUDE)
JAVA_CC         = $(JAVAHOME)/bin/javac
CC              = xlc_r

JAVA_APP        = Prog
JAVA_ARGS       = a b c d e f g

javamain: javamain.c
        $(CC) -go javamain javamain.c $(JAVA_INC)  $(JAVA_LIBS)  -L${PYTHON_LIB_
HOME} -lpython2.3  -bE\:${PYTHON_LIB_HOME}/config/python.exp -bM:SRE -T512 -H512
 -lpthread -lc_r -lm

$(JAVA_APP).class: $(JAVA_APP).java
        $(JAVA_CC) -classpath . $(JAVA_APP).java

run: javamain $(JAVA_APP).class
        $(JAVAHOME)/jre/bin/java -fullversion
        ./javamain $(JAVA_APP) $(JAVA_ARGS)

clean:
        rm -f javamain Prog.class

Вывод данных в Python 2.3

Листинг 9. Вывод данных в Python 2.3
>	export JAVA_EXE=./javamain
>	export AIXTHREAD_SCOPE=S
>	export AIXTHREAD_MUTEX_DEBUG=OFF
>	export AIXTHREAD_RWLOCK_DEBUG=OFF
>	export AIXTHREAD_COND_DEBUG=OFF
>	export LDR_CNTRL=MAXDATA=0x80000000
>	javamain SimpleTime
>	----displayTime(1)
>	----displayTime(2)
>	local time: (2005, 1, 5, 14, 33, 37, 2, 5, 0)

Заключение

Поскольку компании и поставщики программного обеспечения движутся к интеграции продуктов с открытым исходным кодом, потребуется время для понимания требований всех используемых продуктов. В статье была показана ситуация, в которой не работали отличные «коробочные» программы, причем вовсе не из-за того, что какая-либо из них содержит ошибки. При построении бизнес-моделей на основе открытого программного обеспечения, поставщикам этого ПО и клиентам необходимо обратить особое внимание на обеспечение полной совместимости всех используемых продуктов.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX, Технология Java
ArticleID=461668
ArticleTitle=Интеграция Python 2.3 и Java Native Interface в AIX 5L
publish-date=01152010