|
|
|
||||||||||||
|
|
Собственный, легко управляемый и профессионально разработанный сайт – необходимый элемент любого бизнеса. Находитесь ли Вы в командировке, в дороге, дома или на отдыхе имея компьютер, подключенный к сети Internet, Вы получаете доступ к управлению Вашим сайтом! |
|||
C++ и Java: совместное использованиеСтатьи → Программирование на JavaГлубинное родство этих языков программирования позволяет им взаимодействовать, расширяя возможности каждого. Язык JAVA во многом произошел от С/С++, у которых были позаимствованы синтаксис и базовая семантика. Однако связь между ними не ограничивается только этим. Используя JNI (JAVA NATIVE INTERFACE), можно вызывать С/С++ - функции из JAVA-программы и, наоборот, из программы, написанной на С/С++, можно создавать JAVA-объекты и вызывать JAVA-методы. Несмотря на то, что использование JNI в большинстве случаев ведет к потере многоплатформенности JAVA-кода, данная возможность расширяет сферу применения самого языка JAVA на приложения, для которых это условие не является необходимым. В таких системах использование JNI позволяет сочетать современный объектно-ориентированный подход JAVA - главное преимущество этой технологии, с существующим (LEGACY) системно-зависимым (PLATFORM SPECIFIC) кодом на С/С++. Это является важным и необходимым условием перехода к использованию JAVA-технологии при разработке компонентов сервера. Существует несколько причин совместного использования С/С++ и JAVA: стандартные библиотеки JAVA-классов не всегда поддерживают некоторые системно-зависимые возможности; необходимость использования наработанного и отлаженного кода на других языках или желание максимально эффективно реализовать участок кода, критичного с точки зрения времени исполнения. Эти причины не существенны при разработке клиентских приложений, однако в случае серверных - они становятся доминирующими. Для обеспечения интероперабельности программного кода в рамках С/С++ и JAVA JDK1.1 (JAVA DEVELOPERS KIT) предоставляет набор интерфейсов, объединенных в JNI (JAVA NATIVE INTERFACE). JNI позволяет JAVA-коду, исполняемому виртуальной JAVA-машиной (JVM - JAVA VIRTUAL MACHINE), взаимодействовать с приложениями и библиотеками, написанными на языках С/С++ или Ассемблера. Основным преимуществом JNI перед предыдущей версией (JDK 1.0 NI - NATIVE INTERFACE) и другими сходными интерфейсами (NETSCAPE JAVA RINTIME INTERFACE или MICROSOFT'S RAW NATIVE INTERFACE AND COM/JAVA INTERFACE) является то, что JNI изначально разрабатывался для обеспечения двоичной совместимости (BINARY COMPATIBILITY), подразумевающей совместимость приложений, написанных с использованием JNI, для любых JVM на конкретной платформе. Другими словами, один и тот же скомпилированный С/С++-код должен одинаково корректно исполняться JVM NETSCAPE NAVIGATOR и MICROSOFT EXPLORER, SYMANTEC VISUAL CAFО и SUN JAVA WORKSHOP и т.д. для данной платформы (WIN32). Следует отметить, что ранние интерфейсы не удовлетворяли этому условию. Например, JDK 1.0 NI, входящий в JDK 1.0.2 и поддерживаемый в JDK 1.1 для обратной совместимости, использует С-структуры для доступа к членам JAVA-объекта, что определяет зависимость С/С++-кода от того, как JVM располагает объекты в памяти. В общем случае, при использовании JDK 1.0 NI требуется перекомпиляция соответствующего С/С++-кода для каждой JVM на данной платформе. Несмотря на определенную универсальность интерфейса, обусловленную его двоичной совместимостью, JNI обладает широкой функциональностью, предоставляя разработчику все низкоуровневые механизмы JVM: создание JAVA-объектов, включая создание массивов и объектов типа STRING; вызов JAVA-методов; возбуждение и перехват исключительных ситуаций (EXCEPTION); загрузка JAVA-классов и динамический анализ типа (RUNTIME TYPE CHECKING). Отдельно в JNI входит INVOCATION API, позволяющий приложениям динамически загружать JVM. Динамическая загрузка JVM из С/С++-кода позволяет легко встраивать возможности JAVA в существующие системы без необходимости их статического связывания (LINKAGE) с кодом Ниже будет рассмотрено, как создавать коды на С/С++ и JAVA для их совместного использования в рамках JNI и INVOCATION API. Все примеры разработаны и протестированы на платформе WINDOWS 95. Во всех случаях, когда это необходимо, даются пояснения для платформы JNI определяется библиотечными и заголовочными (HEADER) файлами для С/С++. Библиотечные файлы хранятся в подкаталоге LIB (DLL - DYNAMIC-LINK LIBRARY, для WIN32 - в подкаталоге BIN), а заголовочные файлы - в подкаталоге INCLUDE основного каталога Использование JNI Рассмотрим первую из указанных возможностей. Для того чтобы передать управление С/С++-коду из JAVA-программы, необходимо создать собственный JAVA-метод, сгенерировать с помощью утилиты JAVAH заголовочный файл для С/С++-функций, разработать сами функции, в которые будет передаваться управление, и оттранслировать их, поместив в библиотечный файл. После создания библиотеки ее можно загружать из JAVA-программы для последующего вызова собственных методов. Создание собственного JAVA-метода CLASS SYSTEMSPECIFIC { В приведенном примере метод DOSPECIFIC() является собственным, и его С/С++-реализация находится в библиотеке SYSSPEC. Метод LOADLIBRARY() вызывается в статическом инициализаторе, что обеспечивает единственный вызов этого метода после загрузки класса SYSTEMSPECIFIC загрузчиком классов (CLASS LOADER). В принципе, LOADLIBRARY() можно вызывать более одного раза (например, в конструкторе), однако загрузка библиотеки будет происходить только при первом обращении к LOADLIBRARY(), поскольку при последующих вызовах этого метода определяется, что библиотека уже загружена и будет просто возвращаться управление. Метод LOADLIBRARY() преобразует свой параметр в соответствии с тем, как именуются библиотечные файлы на конкретной платформе. В данном примере SYSSPEC преобразуется в SYSSPEC.DLL и LIBSYSSPEC.SO для WIN32 и UNIX соответственно. Метод LOADLIBRARY() использует стандартный алгоритм поиска библиотеки для данной платформы. Для WIN32 DLL должна находиться либо в текущем каталоге процесса, либо в каталоге, содержащем EXE-файл, то есть исполняемый модуль JVM, находящийся в подкаталоге BIN основного каталога JAVA, либо в системном каталоге WIN32, либо каталоге WINDOWS или в каталогах, указанных в переменной окружения PATH. Для UNIX библиотечный файл должен находиться либо в текущем каталоге процесса, либо в подкаталоге LIB основного каталога JAVA, либо в каталогах, перечисленных в переменной окружения LD_LIBRARY_PATH. Если указанную библиотеку найти не удается, метод LOADLIBRARY() генерирует исключительную ситуацию JAVA.LANG.UNSATISFIEDLINKERROR. Однако данная ситуация возникает не только в этом случае. Когда интерпретатор встречает вызов собственного метода, он ищет его (точнее его полную сигнатуру) в списке методов загруженных библиотек. Если метод не найден, то генерируется указанная исключительная ситуация. Для более надежной работы с собственными методами можно использовать, к примеру, следующий код: PUBLIC CLASS APP { Компиляция программ, содержащих собственные методы, ничем не отличается от компиляции обычных программ. Например, если записать предыдущий пример в файл с именем APP.JAVA, то для его компиляции необходимо выполнить следующую команду: C:\ JAVAC APP.JAVA Создание заголовочного файла JAVAH -JNI JAVA.LANG.RUNTIME Перед использованием утилиты JAVAH соответствующий JAVA-класс должен быть скомпилирован в CLASS-файл. Утилита JAVAH анализирует CLASS-файл и строит заголовочный файл, в котором перечислены объявления С/С++-функций, представляющих реализации соответствующих собственных методов. В качестве имен создаваемых заголовочных файлов используются полные квалифицированные имена классов, которые описаны в указанном файле и содержат собственные методы. Например, если выполнить следующие команды: JAVAC APP.JAVA то JAVAH сгенерирует следующий файл /* DO NOT EDIT THIS FILE - IT IS MACHINE GENERATED */ Как указывалось выше, данный файл можно создать вручную или с помощью утилиты JAVAH. В последнем случае не рекомендуется вносить в него какие-либо изменения, так как при последующем применении JAVAH к данному классу все внесенные изменения будут потеряны. Директива препроцессора #INCLUDE <JNI.H> включает файл JNI.H (из подкаталога INLCUDE основного каталога JAVA), в котором находятся все необходимые объявления типов и функций для реализации собственного метода. Макросы JNIEXPORT и JNICALL необходимы только для платформы WIN32, где они раскрываются соответственно в __DECLSPEC(DLLEXPORT) и __STDCALL и позволяют более эффективно строить DLL. Платформа UNIX использует для этих целей обычные С-соглашения, поэтому указанные макросы раскрываются в пустые строки. Как видно из примера, имя С/С++-функции значительно отличается от имени собственного JAVA-метода. Важным понятием при построении имени С/С++-функции и использовании JNI-функций является сигнатура метода (SIGNATURE или METHOD ARGUMENTS Сигнатура метода Таблица 1
Проиллюстрируем эти правила на примерах:
Полная информация о правилах образования сигнатуры метода представлена в файле Правила формирования имени С/С++-функции
Использование имен с сигнатурой на конце необходимо только в случае перегрузки двух или более собственных методов (перегрузка с обычным методом не важна, так как обычные методы не будут находиться в создаваемой библиотеке, что, однако, не допускает наличия собственного и обычного метода с одинаковыми именами и сигнатурами). Для соответствия лексиграфическим правилам С/С++ и использования UNICODE-кодировки, применяются дополнительные правила преобразования, представленные в табл. 2. Таблица 2
Ниже приведен пример JAVA-класса с собственными методами: PACKAGE TESTPACKAGE; и соответствующие им имена С/С++-функций: JNIEXPORT VOID JNICALL JAVA_TESTPACKAGE_TEST_M1___3LJAVA_LANG_STRING_2LJAVA_LANG_OBJECT_2_3_3I Рассмотрим типы параметров, которые получает на входе С/С++-функция при ее вызове. Типы и структуры данных JNI Файл JNI_MD.H содержит системно-зависимые описания JINT, JLONG и JBYTE. В этом же файле определены макросы JNIEXPORT и JNICALL. Тип VOID используется без переопределения. Следует отметить, что для представления строковых объектов JNI использует сокращенный вариант формата UTF-8. Первым аргументом С/С++-функции, представляющей реализацию собственного метода, является указатель на структуру JNIENV. Смысл этого указателя определяет важную идею, лежащую в основе реализации JNI. Если отвлечься от конкретного языка, то указатель на JNIENV, или интерфейсный указатель (JNI INTERFACE POINTER), является указателем на массив указателей, каждый из которых указывает на прикладную функцию JNI. Только через этот указатель С/С++-функция может получить доступ к функциям и ресурсам JNI. В случае С++ (макрос _CPLUSPLUS определен) тип JNIENV является структурой, а в случае С - указателем на структуру. В силу этого, для доступа к функциям JNI в С и С++ применяется различный синтаксис: // C Главным преимуществом такой организации функций JNI является легкость модификации и дальнейшего расширения интерфейса. Указатель на JNIENV действителен только в текущем потоке (THREAD). JVM гарантирует передачу одного и того же интерфейсного указателя всем методам, вызываемым из данного потока. Тем самым запрещено передавать интерфейсный указатель другому потоку. Если методы вызываются из разных потоков, то в этом случае каждый метод получает различные интерфейсные указатели. Если С/С++-функция представляет реализацию нестатического собственного метода, то вторым параметром функции является объект типа JOBJECT. Данный параметр является ссылкой на JAVA-объект, для которого был вызван соответствующий собственный метод. Если функция представляет статический собственный метод, то вторым параметром является объект типа JCLASS, определяющий JAVA-класс, для которого вызван собственный метод класса (CLASS Последующие параметры С/С++-функции соответствуют параметрам собственного метода (если собственный метод их не содержит, то реализующая его С/С++-функция имеет только два описанных выше параметра). JNI функции
Использование JNI функций необходимо только в том случае, если С/С++-функция осуществляет какое-либо взаимодействие с JVM: вызов JAVA-методов, доступ к данным, создание JAVA-объектов и т.д. Ниже приведен пример JAVA-программы, которая выводит на печать количество свободной памяти на диске С для платформы WIN32. Для этого используется собственный метод и соответствующая реализационная С/С++-функция, вызывающая при своей работе функцию WIN32 // Файл APP.JAVA Для успешной компиляции кода JAVA и С/С++ на платформе WIN32 необходимо правильно установить переменные окружения PATH, LIB, INCLUDE и CLASSPATH (многие из этих значений можно задать как параметры соответствующих компиляторов). Если записать исходные тексты предыдущего примера в файлы APP.JAVA и SYSTEMSPECIFIC.CPP соответственно, то для их компиляции необходимо выполнить следующие команды (предполагается, что исходные файлы находятся в каталоге C:\TEST\NATIVE> JAVAC APP.JAVA Для запуска программы необходимо выполнить: C:\TEST\NATIVE> JAVA APP Для трансляции С/С++-файлов можно использовать любой компилятор, допускающий создание 32-битных Использование INVOCATION API В общем случае, для встраивания JVM в программу ее необходимо создать и проинициализировать, присоединить, если это необходимо, к какому-либо потоку, а по окончании работы с JVM удалить ее из памяти процесса. После того как JVM создана и получен интерфейсный указатель, можно использовать любые JNI-функции. Рассмотрим пример С++-кода, который создает JVM в процессе своей работы и вызывает статический метод JAVA-класса. Ниже приведены исходные тексты JAVA-класса и C++-кода: // Файл INVOCATIONAP.JAVAI Для компиляции приведенной программы на платформе WIN32 необходимо выполнить следующие команды (предполагается, что переменные окружения PATH, LIB, INCLUDE и CLASSPATH установлены верно и исходные файлы находятся в каталоге C:\TEST\NATIVE\ JAVAC INVOCATIONAPI.JAVA А для запуска программы: C:\TEST\NATIVE\ INVOCATIONAPI На первый взгляд, наличие JNI и полная публикация его спецификаций может привлечь разработчиков к написанию непереносимого JAVA-кода, что, в свою очередь, может значительно снизить эффективность применения технологии JAVA. Однако на самом деле JNI способствует обратному процессу. Практически для любого приложения можно совершенно точно определить необходимость в многоплатформенном исполнении. Для систем, которые не нуждаются в многоплатформенности, JNI предоставляет инфраструктуру, с помощью которой JAVA-приложение может взаимодействовать с операционной системой и аппаратурой, в среде которых оно исполняется. Таким образом, JNI является естественным дополнением JAVA-технологии. Он позволяет использовать ее как для создания переносимых (клиентских) приложений, так и для создания высокопроизводительных (серверных) систем, использующих всю специфику конкретной платформы и аппаратуры, сохраняя в то же время главное достоинство JAVA - современный объектно-ориентированный подход. Автор: Никита Иванов 16.02.2006 |
VSESMI.ru — новости в СМИ.
Tur-Hotel.ru — отзывы об отелях
|
|
Copyright © 2002—2010 ООО "Хостмэйк" Телефон в Москве: +7 (495) 223-46-50 Телефон в Санкт-Петербурге: +7 (812) 448-38-90 Тел./Факс: +7 (8636) 237-836 E-mail: 2006 |