Соединяем Java и .NET: jni4net

Written by elwood

jni4net143x123 Наша система, написанная на Java, должна взаимодействовать с коллцентром. Коллцентр – это такая десктопная приложуха, которая запускается на компьютере отдельно и принимает звонки клиентов. Приложение это предоставляет COM-интерфейсы для взаимодействия с ней, можно получить текущий статус, подключиться к серверу и подписаться на события поступления звонков. Так как взаимодействовать с COM удобнее всего на дотнете, то решили действовать именно так. Но основное-то наше приложение – это десктопный Swing-клиент! Нужно как-то транслировать всю информацию из дотнетовой аппликухи в свинговую. Два года назад такая же проблема была решена путём взаимодействия через сеть. Дотнетовое приложение тогда запускалось отдельно и при некоторых событиях дёргало по HTTP некий урл, который уже обрабатывался Java-программой. Но сейчас мы обнаружили такую замечательную штуку как jni4net. Этот opensource инструмент, написанный энтузиастом, позволяет внутри Java-процесса загружать дотнетовую CRL и работать напрямую с дотнетовыми классами через JNI. Для дотнетовых классов будут сгенерированы удобные Java-прокси классы, которые вы сможете вызывать так, как будто это были бы обычные Java классы. Но при этом все вызовы будут затранслированы в дотнетовский рантайм. То же самое можно проделать и в другом направлении – то есть внутри CLR подгрузить Java Runtime и обращаться к JVM аналогичным образом. Поковырявшись немного, хочу тезисно расписать основные моменты, на которых бы хотелось остановиться:

  1. Это реально работает!
  2. Чтобы создать ant-скрипт для сборки такой конструкции, пришлось повозиться, но я подготовил шаблон проекта, с которого можно начать быстро и безболезненно.
  3. В случае вызова .NET из контекста работающего Java-процесса все происходит примерно следующим образом: jni4net загружает DLL-переходник для CLR соответствующей разрядности, затем происходит загрузка CLR, после этого грузится ваша DLL.
  4. Дотнетовые поля не маршаллятся (в прокси-классах их просто не будет), используйте свойства.
  5. Свойства маршаллятся на ура, превращаясь по ходу дела в геттеры и сеттеры (правда, булевые свойства тоже будет доступны через get, а не через is, как многие привыкли видеть).
  6. Делегаты нужно придумывать свои, поскольку прокси-классы к стандартным скорее всего не будут созданы (для этого наверное нужно более подробно конфигурировать proxygen). То есть просто EventHandler лучше не использовать, можно завести свой public делегат и он прекрасно будет преобразован в интерфейс с методом Invoke(). Соответственно, в Java коде вы создаете свою реализацию этого интерфейса (например, анонимную) – всё как с обычными джавовыми слушателями.
  7. Насчет исключений я не разбирался, вроде бы тоже должны маршаллиться, но я предпочел использовать паттерн “GetLastError”, чтобы не зависеть от этого.
  8. Если ваша DLL требует того, чтобы быть сконфигурированной в App.config- у вас не получится этого сделать. В списках рассылки автор предлагает обходить это двумя путями: либо инициализировать всё программно, либо в вашей DLL уже создавать отдельный AppDomain и в него загружать сборки.
  9. Если вам необходимо работать с 32-разрядными библиотеками (в моём случае это было именно так), то вам придётся позаботиться о том, как настроить запуск вашего java-приложения с 32-битной JVM.
  10. Если вы хотите засунуть все DLL в отдельную папку, вы должны туда же засунуть и jni4net.j-0.8.6.0.jar. Судя по экспериментам, jni4net ищет свой рантайм рядом с собой, определяя местоположение своего джарника. Но при указании библиотеки, которую вы хотите загрузить, вы должны указать путь к ней. Например, так:Bridge.LoadAndRegisterAssemblyFrom(new java.io.File("lib\\net-app.j4n.dll"));

    Если всё, что связано с jni4net лежит в lib. В этом случае jni4net найдет свой рантайм, а также найдет net-app.j4n.dll в месте, которое вы ему указали. Можно ли держать рантайм jni4net в одной папке, а дотнетовые DLL в другой, пока не ясно.

  11. Запуск из папки, расположенной на сетевом диске, не работает – Windows не разрешает загружать дотнетовые сборки из shared-папок по умолчанию. На Windows 7 я получил следующий стектрейс:
    Can't init BridgeExport: DLLs are marked as unsafe. Open file properties in windows explorer and click unblock.
    Can't init BridgeExport:An attempt was made to load an assembly from a network location which would have caused the assembly to be
    sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not enable CAS policy by default, s
    o this load may be dangerous. If this load is not intended to sandbox the assembly, please enable the loadFromRemoteSources switch
    . See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
    Can't init BridgeExport:System.NotSupportedException: An attempt was made to load an assembly from a network location which would
    have caused the assembly to be sandboxed in previous versions of the .NET Framework. This release of the .NET Framework does not e
    nable CAS policy by default, so this load may be dangerous. If this load is not intended to sandbox the assembly, please enable th
    e loadFromRemoteSources switch. See http://go.microsoft.com/fwlink/?LinkId=155569 for more information.
    at System.Reflection.RuntimeAssembly.nLoadFile(String path, Evidence evidence)
    at System.Reflection.Assembly.LoadFile(String path)
    at net.sf.jni4net.BridgeExport.initDotNet(IntPtr envi, IntPtr clazz)
    Can't initialize jni4net Bridge from S:\temp\elwood\Taxi\lib\jni4net.n.w32.v40-0.8.6.0.dll
    Can't initialize jni4net BridgeCan't initialize jni4net Bridge. Code:-101

    На Windows XP у меня в такой же ситуации просто вылетела JVM. Возможно, есть способ это исправить, но я не исследовал.

 

Ещё раз ссылка на шаблон проекта, который можно взять и запустить:

https://bitbucket.org/igor_kostromin/jni4net-app-template