Re: JNI Attach
От: GarryIV  
Дата: 04.11.24 17:57
Оценка: 2 (1) +2
Здравствуйте, Ablex, Вы писали:

A>Java-приложение (назовем его "главное" приложение) загружает библиотеку на C++, которая грузит плагины (тоже на C++)

A>Далее хотелось бы из некоторых плагинов (видимо используя JNI) приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR файлов.

A>Можно такое (выделенное жирным) сделать и как?


Резюмируя срач дискуссию
* загрузить jar в java нельзя. в java загружаются классы и ресурсы. для загрузки классов нужен класслоадер. Для загрузки из jar файлов обычно используется стандартный URLClassLoader
* самое простое это настроить этот самый класслоадер при старте JVM (см параметры команды java в частности cp)

От себя
* динамическая загрузка классов подразумевает динамическую же выгрузку классов — отдельный головняк
* схема в целом выглядит кринжово, я бы не стал так делать. если можешь стартовать свой си++ код в отдельном процессе то так и делай, не морочь голову.
WBR, Igor Evgrafov
Re: JNI Attach
От: · Великобритания  
Дата: 04.11.24 12:25
Оценка: 2 (1) +1
Здравствуйте, Ablex, Вы писали:

A> В общем схема такая:

A> Java-приложение (назовем его "главное" приложение) загружает библиотеку на C++, которая грузит плагины (тоже на C++)
A> Далее хотелось бы из некоторых плагинов (видимо используя JNI) приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR файлов.
В java не бывает никакой догрузки JAR-файлов. Есть ClassLoader-ы и они резолвят классы, отыскивая их в class paths. Как-то так.

A> Можно такое (выделенное жирным) сделать и как?

При регистрации плагина, ему нужно давать какой-то интерфейс, который они могут позвать с целью создания своего класслоадера с нужными источниками классов и т.д.

Но вообще говоря, это довольно сложный путь с кучей подводных граблей с жонглированием версиями зависимостей. Я бы лучше смотрел в сторону запуска отдельных процессов-сервисов и общению через пайпы/сокеты/shmem.
Очевидная грабля — старинный плагин хочет непременно JDK1.7, а новый плагин захочет JDK21 — и приехали.

JNI_GetCreatedJavaVMs — это что-то совсем другое, это когда из нативного кода запускаем VM. Я не понимаю как такое можно приспособить к твоей задаче.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 12:45
Оценка: 2 (1) +1
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Ablex, Вы писали:

·>Но вообще говоря, это довольно сложный путь с кучей подводных граблей с жонглированием версиями зависимостей. Я бы лучше смотрел в сторону запуска отдельных процессов-сервисов и общению через пайпы/сокеты/shmem.
·>Очевидная грабля — старинный плагин хочет непременно JDK1.7, а новый плагин захочет JDK21 — и приехали.

Зачем плагину JDK ? Он же на С++ написан.

Надо получить JavaVM **vm в JNI-библиотеке, а потом то ли из нее, то ли из плагина попросить Java VM (вызвать какой-то метод?) загрузить jar через ClassLoader, как в той ссылке, что ты привел.
With best regards
Pavel Dvorkin
Re[2]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 12:18
Оценка: 2 (1)
Здравствуйте, Ablex, Вы писали:

A>JNI_GetCreatedJavaVMs — оно?


Я отнюдь не специалист по JNI.

Думаю, что скорее GetJavaVM

https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetJavaVM

Посмотри здесь

https://stackoverflow.com/questions/12900695/how-to-obtain-jni-interface-pointer-jnienv-for-asynchronous-calls
With best regards
Pavel Dvorkin
Re[4]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 12:27
Оценка: 2 (1)
Здравствуйте, Ablex, Вы писали:


A>Если ещё появятся еще какие-то комментарии, то буду благодарен.


Похоже, вот то, что тебе нужно

https://www.codeproject.com/Articles/993067/Calling-Java-from-Cplusplus-with-JNI
With best regards
Pavel Dvorkin
Re[9]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 14:23
Оценка: 2 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> ·>Так ведь тут не приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR, а создать новый класслоадер с новыми источниками классов. Разница принципиальная.

PD> Ну и что ? Надо как-то из C/C++ библиотеки/плугина вызывать некий метод из java класса, а он пусть делает то, что в
Ну в общем да. Это я и сказал в самом начале: "При регистрации плагина, ему нужно давать какой-то интерфейс, который они могут позвать с целью создания своего класслоадера с нужными источниками классов и т.д.". Т.е. плагин должен просто подёргать некие java-методы, как обычно. Загрузка классов никакого отношения к сабж не имеет.

PD> Надо новый класслоадер делать — пусть делает, какая разница ?

Что это ничего в classpath VM не догружает. Это такой специальный system classpath, иммутабельный.

PD> ·>"Ну может, тогда и не нужен GetJavaVM" — верно, что я сразу и сказал.

PD> Я в первом же ответе написал, что отнюдь не специалист по JNI. И то, что JNIEnv передается в любой метод, я просто не знал. Если так, то все еще проще.
Именно. Поэтому твои рекомендации использовать GetJavaVM не в ту степь.

PD> Там неверно ?

Верно, но к JNI никакого отношения не имеет.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[10]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 14:37
Оценка: 2 (1)
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>> ·>Так ведь тут не приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR, а создать новый класслоадер с новыми источниками классов. Разница принципиальная.

PD>> Ну и что ? Надо как-то из C/C++ библиотеки/плугина вызывать некий метод из java класса, а он пусть делает то, что в
·>Ну в общем да. Это я и сказал в самом начале: "При регистрации плагина, ему нужно давать какой-то интерфейс, который они могут позвать с целью создания своего класслоадера с нужными источниками классов и т.д.". Т.е. плагин должен просто подёргать некие java-методы, как обычно. Загрузка классов никакого отношения к сабж не имеет.

Не имеет. Это потом этот метод и должен делать.

PD>> Надо новый класслоадер делать — пусть делает, какая разница ?

·>Что это ничего в classpath VM не догружает. Это такой специальный system classpath, иммутабельный.

Да ради бога, не имеет значения. Важно, что можно в конечном счете сделать то, что нужно ТС.

PD>> ·>"Ну может, тогда и не нужен GetJavaVM" — верно, что я сразу и сказал.

PD>> Я в первом же ответе написал, что отнюдь не специалист по JNI. И то, что JNIEnv передается в любой метод, я просто не знал. Если так, то все еще проще.
·>Именно. Поэтому твои рекомендации использовать GetJavaVM не в ту степь.

Равно как и твои насчет версий JDK .

PD>> Там неверно ?

·>Верно, но к JNI никакого отношения не имеет.

В общем, похоже, мы говорим об одном и том же. Суть от этого не меняется. Про то, что JVM не нужна — уже написал раньше.

Сама задача , видимо, решается.

С остальным пусть ТС сам разбирается. Думаю, мы вдвоем дали ему достаточно материала для дальнейших изысканий.
With best regards
Pavel Dvorkin
Re[12]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 16:33
Оценка: 2 (1)
Здравствуйте, ·, Вы писали:

·>Это имеет значение по нескольким причинам. Главное тут, что он, похоже, собирается организовывать интеграцию частей от разных источников/вендоров плагинов. И тут возникает проблема, что все они одновременно должны работать вместе и как-то взаимодействовать.


·>Этим наелись уже давно, во времена application servers и прочего энтерпрайза. Мороки много, хитрые грабли вылазят. А если ещё и выгрузка плагинов/классов...


·>Версия JDK: представь себе какой-то старинный, но очень нужный плагин где-то там через зависимости хочет sun.misc.*, который доступен в JDK 7. И всё работает. А потом выходит новый и ещё более нужный плагин на JDK 21, а там какой-то класс deprecated и removed.


·>Вдобавок, кастомные ClassLoader-ы требуют осторожного обращения и понимания что происходит. Например, один и тот же класс загруженный разными класслоадерами — по сути два разных класса, у которых, например, свои глобальные переменные и хитрые исключения вроде cannot cast my.cool.SomeClass to my.cool.SomeClass.


А вот с этим соглашусь, конечно, без сомнения.
Собственно, я уже собрался спать ложиться, но вспомнил об этом и решил еще один ответ написать. Но теперь не буду, так как ты изложил все, что я собирался написать.

Кстати, и не только экзотика в виде sun.misc. И не только версии JDK. А еще и просто зависимости от прочих артефактов, которые могут быть в основной java программе и других jar различными по версии. Если они там окажутся одновременно в итоге загрузки, то хорошего будет тоже мало.

Впрочем, это к JNI не имеет отношения, к плагинам на C/C++ и к runtime-загрузке jar тоже. Это обычная проблема на java. Если бы он просто добавил все свои jar в java — проект без всякого C/C++, было бы то же самое.

Так что советую ТС проверить

mvn dependency:tree
mvn dependency:analyze-duplicate

и для основной java программы и для всех jar

https://www.baeldung.com/maven-duplicate-dependencies
https://stackoverflow.com/questions/4417820/how-to-list-dependencies-of-a-jar

Кстати, вопрос к ТС. А почему нужно загружать все эти jar-плагины таким сложным способом ? Нельзя их просто загрузить в java приложение статически ?
With best regards
Pavel Dvorkin
Отредактировано 04.11.2024 16:39 Pavel Dvorkin . Предыдущая версия .
Re[13]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 17:48
Оценка: 2 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> Кстати, и не только экзотика в виде sun.misc.

А ещё весёлые матрёшки как в jni плагине jar-зависимость, которая испольует jni... happy degugging.

PD> И не только версии JDK. А еще и просто зависимости от прочих артефактов, которые могут быть в основной java программе и других jar различными по версии. Если они там окажутся одновременно в итоге загрузки, то хорошего будет тоже мало.

Формально этот самый кастомный ClassLoader можно создавать "чистым", т.е. ты контролируешь что туда попадает и иметь плагины изолированными от всего, кроме JRE, системного classpath.
Но скорее всего плагинам надо между собой как-то взаимодействовать (вопрос к топикстартеру) и тут придётся серьёзно продумать архитектуру... и, скорее всего получится кошмар в духе OSGi. Более разумным решением, имхо, будет явное взаимодействие через какие-нибудь сообщения protobuf или подобное.

PD> Впрочем, это к JNI не имеет отношения, к плагинам на C/C++ и к runtime-загрузке jar тоже. Это обычная проблема на java. Если бы он просто добавил все свои jar в java — проект без всякого C/C++, было бы то же самое.

Угу. Хотя это не проблема конкретно java, а общая проблема сборки гигантских монолитных приложений из множества разрозненных компонент. А тут ещё и кросс-языковое приложение java/c++.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[6]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 13:15
Оценка: +1
Здравствуйте, ·, Вы писали:

·>Похоже, что нет. У него java-приложение (назовем его "главное" приложение) загружает библиотеку на C++, а по твоей ссылке с точностью до наоборот: C++ приложение загружает JVM.


Да, но скорее всего основная замена — это JNI_CreateJavaVM на GetJavaVM.
ТС нужно не создавать и запускать JVM , а добраться к JVM, которая вызвала его JNI. А остальное все так же.
With best regards
Pavel Dvorkin
JNI Attach
От: Ablex  
Дата: 04.11.24 11:36
Оценка:
Здрасьте)

Я в java мало что понимаю, но собираюсь разбираться, и хотелось бы получить некоторые ответы.

В общем схема такая:
Java-приложение (назовем его "главное" приложение) загружает библиотеку на C++, которая грузит плагины (тоже на C++)
Далее хотелось бы из некоторых плагинов (видимо используя JNI) приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR файлов.

Можно такое (выделенное жирным) сделать и как?
Re: JNI Attach
От: Ablex  
Дата: 04.11.24 11:51
Оценка:
A>Можно такое (выделенное жирным) сделать и как?

JNI_GetCreatedJavaVMs — оно?
Re[3]: JNI Attach
От: Ablex  
Дата: 04.11.24 12:24
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Посмотри здесь


PD>https://stackoverflow.com/questions/12900695/how-to-obtain-jni-interface-pointer-jnienv-for-asynchronous-calls


Спасибо! Буду разбираться.

Нашёл ещё вот такой способ:
https://stackoverflow.com/questions/50246855/get-java-vm-pointer-in-global-object-constructor-in-android-native-code-before-j

Если ещё появятся еще какие-то комментарии, то буду благодарен.
Re[3]: JNI Attach
От: Ablex  
Дата: 04.11.24 12:50
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Надо получить JavaVM **vm в JNI-библиотеке, а потом то ли из нее, то ли из плагина попросить Java VM (вызвать какой-то метод?) загрузить jar через ClassLoader, как в той ссылке, что ты привел.


Да, вы всё верно понимаете.
Re[5]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 13:05
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> A>Если ещё появятся еще какие-то комментарии, то буду благодарен.

PD> Похоже, вот то, что тебе нужно
Похоже, что нет. У него java-приложение (назовем его "главное" приложение) загружает библиотеку на C++, а по твоей ссылке с точностью до наоборот: C++ приложение загружает JVM.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[3]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 13:13
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> ·>Очевидная грабля — старинный плагин хочет непременно JDK1.7, а новый плагин захочет JDK21 — и приехали.

PD> Зачем плагину JDK ? Он же на С++ написан.
Он сказал, что плагин хочет загрузить какие-то свои дополнительные java-классы, которых нет в "главном приложении" которое уже запущено. Эти самые java-классы могут быть не совместимы с JDK, под которым работает его главное приложение.

PD> Надо получить JavaVM **vm в JNI-библиотеке, а потом то ли из нее, то ли из плагина попросить Java VM (вызвать какой-то метод?)

Накой ему **vm? И что за метод-то ты предлагаешь вызвать-то?

PD> загрузить jar через ClassLoader, как в той ссылке, что ты привел.

Выражайся точнее. jar не загружается. Загружаются классы из различных источников (возможно из jar-ов). И динамически менять classpath нельзя (если исключить грязные хаки).
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 13:21
Оценка:
Здравствуйте, ·, Вы писали:

PD>> Зачем плагину JDK ? Он же на С++ написан.

·>Он сказал, что плагин хочет загрузить какие-то свои дополнительные java-классы, которых нет в "главном приложении" которое уже запущено. Эти самые java-классы могут быть не совместимы с JDK, под которым работает его главное приложение.

Нет. Цитирую его

которая грузит плагины (тоже на C++)
Далее хотелось бы из некоторых плагинов (видимо используя JNI) приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR файлов.


Приаттачиться к VM и попросить ее догрузить. Ее (то есть java в ней), а вовсе не самому плагину это делать.


PD>> Надо получить JavaVM **vm в JNI-библиотеке, а потом то ли из нее, то ли из плагина попросить Java VM (вызвать какой-то метод?)

·>Накой ему **vm? И что за метод-то ты предлагаешь вызвать-то?

Какого-то класса из java приложения (не из этих jar, а из основного java приложения), который и будет все это делать. По ссылке есть примеры как это делать.

PD>> загрузить jar через ClassLoader, как в той ссылке, что ты привел.

·>Выражайся точнее. jar не загружается. Загружаются классы из различных источников (возможно из jar-ов). И динамически менять classpath нельзя (если исключить грязные хаки).

Спасибо за уточнение терминологии.
With best regards
Pavel Dvorkin
Отредактировано 04.11.2024 13:21 Pavel Dvorkin . Предыдущая версия .
Re[7]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 13:29
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> ·>Похоже, что нет. У него java-приложение (назовем его "главное" приложение) загружает библиотеку на C++, а по твоей ссылке с точностью до наоборот: C++ приложение загружает JVM.

PD> Да, но скорее всего основная замена — это JNI_CreateJavaVM на GetJavaVM.
PD> ТС нужно не создавать и запускать JVM , а добраться к JVM, которая вызвала его JNI. А остальное все так же.
"Всё так же" что конкретно??? Ну вот есть у него *vm. И?

Единственное что можно существенного делать с этим — это GetEnv, но *env и так передаётся первым аргументом в каждый нативный метод.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[5]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 13:35
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> Нет. Цитирую его

PD> которая грузит плагины (тоже на C++)
PD> Далее хотелось бы из некоторых плагинов (видимо используя JNI) приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR файлов.
PD> Приаттачиться к VM и попросить ее догрузить. Ее (то есть java в ней), а вовсе не самому плагину это делать.
На что я и ответил, что такого понятия как "догрузить туда еще пачку JAR" в VM не существует в спеке Явы. Ну, по крайней мере, я не в курсе.

PD> ·>Накой ему **vm? И что за метод-то ты предлагаешь вызвать-то?

PD> Какого-то класса из java приложения (не из этих jar, а из основного java приложения), который и будет все это делать. По ссылке есть примеры как это делать.
По ссылке пример как запустить новую VM с данным optionString = "-Djava.class.path=.". Поменять это после запуска нельзя, нет такого API. Это по сути command line args, аналог java.exe -Djava.class.path=..
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 13:42
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Pavel Dvorkin, Вы писали:


·>"Всё так же" что конкретно??? Ну вот есть у него *vm. И?


jint GetJavaVM(JNIEnv *env, JavaVM **vm);

Returns the Java VM interface (used in the Invocation API) associated with the current thread. The result is placed at the location pointed to by the second argument, vm.

https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#GetJavaVM

·>Единственное что можно существенного делать с этим — это GetEnv, но *env и так передаётся первым аргументом в каждый нативный метод.


Ну может, тогда и не нужен GetJavaVM, раз и так передается.

А что делать —



jclass cls2 = env->FindClass("MyTest");  // try to find the class
if(cls2 == nullptr) {
    cerr << "ERROR: class not found !";
}
else {                                  // if class found, continue
    cout << "Class MyTest found" << endl;
    jmethodID mid = env->GetStaticMethodID(cls2, "mymain", "()V");  // find method
    if(mid == nullptr)
        cerr << "ERROR: method void mymain() not found !" << endl;
    else {
        env->CallStaticVoidMethod(cls2, mid);                      // call method
        cout << endl;
    }

}

https://www.codeproject.com/Articles/993067/Calling-Java-from-Cplusplus-with-JNI
With best regards
Pavel Dvorkin
Re[6]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 13:47
Оценка:
Здравствуйте, ·, Вы писали:

PD>> Приаттачиться к VM и попросить ее догрузить. Ее (то есть java в ней), а вовсе не самому плагину это делать.

·>На что я и ответил, что такого понятия как "догрузить туда еще пачку JAR" в VM не существует в спеке Явы. Ну, по крайней мере, я не в курсе.

Ты же сам дал ссылку на то, как через classpath их добавить.

Вот еще пример, как добавить, правда, какой-то сабкласс, но это едва ли существенно.

https://stackabuse.com/example-loading-a-java-class-at-runtime/

PD>> Какого-то класса из java приложения (не из этих jar, а из основного java приложения), который и будет все это делать. По ссылке есть примеры как это делать.

·>По ссылке пример как запустить новую VM с данным optionString = "-Djava.class.path=.". Поменять это после запуска нельзя, нет такого API. Это по сути command line args, аналог java.exe -Djava.class.path=..

Ответил в другом ответе.
With best regards
Pavel Dvorkin
Re[7]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 14:00
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> ·>На что я и ответил, что такого понятия как "догрузить туда еще пачку JAR" в VM не существует в спеке Явы. Ну, по крайней мере, я не в курсе.

PD> Ты же сам дал ссылку на то, как через classpath их добавить.
PD> Вот еще пример, как добавить, правда, какой-то сабкласс, но это едва ли существенно.
PD> https://stackabuse.com/example-loading-a-java-class-at-runtime/
Так ведь тут не приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR, а создать новый класслоадер с новыми источниками классов. Разница принципиальная.

PD> Ответил в другом ответе.

"Ну может, тогда и не нужен GetJavaVM" — верно, что я сразу и сказал.

PD> А что делать — jclass cls2 = env->FindClass("MyTest"); // try to find the class

ИЧСХ тут никакого "догрузить туда еще пачку JAR" нет. Классы будут искаться только из текущей запущенной VM, ничего догруженного тут быть не может.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 04.11.24 14:08
Оценка:
Здравствуйте, ·, Вы писали:

·>Так ведь тут не приаттачиться к VM из "главного" приложения и догрузить туда еще пачку JAR, а создать новый класслоадер с новыми источниками классов. Разница принципиальная.


Ну и что ? Надо как-то из C/C++ библиотеки/плугина вызывать некий метод из java класса, а он пусть делает то, что в

public C LoadClass(String directory, String classpath, Class<C> parentClass) throws ClassNotFoundException {

Надо новый класслоадер делать — пусть делает, какая разница ?

·>"Ну может, тогда и не нужен GetJavaVM" — верно, что я сразу и сказал.


Я в первом же ответе написал, что отнюдь не специалист по JNI. И то, что JNIEnv передается в любой метод, я просто не знал. Если так, то все еще проще.

PD>> А что делать — jclass cls2 = env->FindClass("MyTest"); // try to find the class

·>ИЧСХ тут никакого "догрузить туда еще пачку JAR" нет. Классы будут искаться только из текущей запущенной VM, ничего догруженного тут быть не может.

Ну тут я не понимаю. Я же дал ссылку, где из directory их загружают.

Here is some code that will help you dynamically load a new class given the path to its jar file.

https://stackabuse.com/example-loading-a-java-class-at-runtime/

Там неверно ?
With best regards
Pavel Dvorkin
Отредактировано 04.11.2024 14:09 Pavel Dvorkin . Предыдущая версия .
Re[11]: JNI Attach
От: · Великобритания  
Дата: 04.11.24 16:13
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> Да ради бога, не имеет значения. Важно, что можно в конечном счете сделать то, что нужно ТС.

Ну да.

PD> ·>Именно. Поэтому твои рекомендации использовать GetJavaVM не в ту степь.

PD> Равно как и твои насчет версий JDK .
Это имеет значение по нескольким причинам. Главное тут, что он, похоже, собирается организовывать интеграцию частей от разных источников/вендоров плагинов. И тут возникает проблема, что все они одновременно должны работать вместе и как-то взаимодействовать.

Этим наелись уже давно, во времена application servers и прочего энтерпрайза. Мороки много, хитрые грабли вылазят. А если ещё и выгрузка плагинов/классов...

Версия JDK: представь себе какой-то старинный, но очень нужный плагин где-то там через зависимости хочет sun.misc.*, который доступен в JDK 7. И всё работает. А потом выходит новый и ещё более нужный плагин на JDK 21, а там какой-то класс deprecated и removed.

Вдобавок, кастомные ClassLoader-ы требуют осторожного обращения и понимания что происходит. Например, один и тот же класс загруженный разными класслоадерами — по сути два разных класса, у которых, например, свои глобальные переменные и хитрые исключения вроде cannot cast my.cool.SomeClass to my.cool.SomeClass.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[14]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 05.11.24 00:39
Оценка:
Здравствуйте, ·, Вы писали:

·>А ещё весёлые матрёшки как в jni плагине jar-зависимость, которая испольует jni... happy degugging.




PD>> И не только версии JDK. А еще и просто зависимости от прочих артефактов, которые могут быть в основной java программе и других jar различными по версии. Если они там окажутся одновременно в итоге загрузки, то хорошего будет тоже мало.

·>Формально этот самый кастомный ClassLoader можно создавать "чистым", т.е. ты контролируешь что туда попадает и иметь плагины изолированными от всего, кроме JRE, системного classpath.
·>Но скорее всего плагинам надо между собой как-то взаимодействовать (вопрос к топикстартеру) и тут придётся серьёзно продумать архитектуру... и, скорее всего получится кошмар в духе OSGi. Более разумным решением, имхо, будет явное взаимодействие через какие-нибудь сообщения protobuf или подобное.

Может быть и protobuf, может, и просто что-то из IPC, но это вопрос к ТС. И это значит все же несколько процессов. Мы же не знаем, зачем эти jar подгружать вообще, что они делать должны. Более того, и плагинами их можно называть с натяжкой. Зачем их нужно загружать, почему именно по требованию и что они будут делать — этого мы не знаем. Может, вообще есть более простое решение.

PD>> Впрочем, это к JNI не имеет отношения, к плагинам на C/C++ и к runtime-загрузке jar тоже. Это обычная проблема на java. Если бы он просто добавил все свои jar в java — проект без всякого C/C++, было бы то же самое.

·>Угу. Хотя это не проблема конкретно java, а общая проблема сборки гигантских монолитных приложений из множества разрозненных компонент. А тут ещё и кросс-языковое приложение java/c++.

Ну кроссплатформенное он постольку-поскольку. В той модели, что предложил ТС, java и C++ части достаточно хорошо отделены. Но вообще да. Не знаю, почему в java столь усложнили динамическую загрузку кода. В C++ (точнее, в Win API) загрузка и выгрузка DLL в рантайме совершенно штатный и банальный процесс, на то они и DLL. И грузить их можно из любого места на диске, без всяких classpath и лоадеров. Но если EXE использует одну версию C++ RTL, а DLL другую, то мало никому не покажется, если, например, выделять память в DLL и освобождать ее в EXE.
With best regards
Pavel Dvorkin
Re[15]: JNI Attach
От: · Великобритания  
Дата: 05.11.24 23:07
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Ну кроссплатформенное он постольку-поскольку. В той модели, что предложил ТС, java и C++ части достаточно хорошо отделены. Но вообще да. Не знаю, почему в java столь усложнили динамическую загрузку кода. В C++ (точнее, в Win API) загрузка и выгрузка DLL в рантайме совершенно штатный и банальный процесс, на то они и DLL. И грузить их можно из любого места на диске, без всяких classpath и лоадеров. Но если EXE использует одну версию C++ RTL, а DLL другую, то мало никому не покажется, если, например, выделять память в DLL и освобождать ее в EXE.

Потому что в java загружается не код, а класс (ClassLoader же!), а значит тут тебе и статические переменные, и сборка мусора. И проблема собственно не в загрузке, а в общем дизайне решения, архитектуре системы, когда загружается много чего и начинает друг с другом взаимодейстовать. Так что по честному надо не с DLL сравнивать, а хотя бы с COM.
Да и вообще, ничего усложнённого я не вижу, две строчки кода. Гораздо проще, чем загружать dll — по сути указали источники, взяли класс по имени и готово.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[16]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 06.11.24 04:05
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Pavel Dvorkin, Вы писали:


·>Потому что в java загружается не код, а класс (ClassLoader же!), а значит тут тебе и статические переменные, и сборка мусора. И проблема собственно не в загрузке, а в общем дизайне решения, архитектуре системы, когда загружается много чего и начинает друг с другом взаимодейстовать. Так что по честному надо не с DLL сравнивать, а хотя бы с COM.


Отчасти согласен. В DLL, конечно, нет уже фактически классов, только код и данные. Впрочем, и это не совсем так. Экспортируемые классы есть, вот от внутренних действительно почти ничего не осталось. И сборки мусора, конечно, тоже нет. Но там тоже всякого хватает. Статические переменные тоже есть, более того, есть shared данные , это один из механизмов IPC — через общую секцию в DLL. Да и DllMain вообще-то не подарок, со всеми правилами, что можно в ней делать, а что нет. Плюс еще загрузка всех import-DLL этой DLL и рекурсивно далее.
Но COM, да, больше подходит для сравнения.

·>Да и вообще, ничего усложнённого я не вижу, две строчки кода. Гораздо проще, чем загружать dll — по сути указали источники, взяли класс по имени и готово.


А почему действительно нет загрузки jar ? Он же по сути аналог DLL/COM тут — набор клаcсов. Почему нельзя его просто загрузить ? При статическом подключении (явно или через pom.xml) фактически он же загружается со всеми своими потрохами.
With best regards
Pavel Dvorkin
Отредактировано 06.11.2024 4:14 Pavel Dvorkin . Предыдущая версия .
Re[17]: JNI Attach
От: · Великобритания  
Дата: 06.11.24 09:38
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> Отчасти согласен. В DLL, конечно, нет уже фактически классов, только код и данные. Впрочем, и это не совсем так. Экспортируемые классы есть, вот от внутренних действительно почти ничего не осталось.

Классы в dll? Гы, такой гемор. Обычно из юзабельного только extern "C".

PD> Но COM, да, больше подходит для сравнения.

Угу, и в COM гораздо более грустно.

PD> ·>Да и вообще, ничего усложнённого я не вижу, две строчки кода. Гораздо проще, чем загружать dll — по сути указали источники, взяли класс по имени и готово.

PD> А почему действительно нет загрузки jar ? Он же по сути аналог DLL/COM тут — набор клаcсов. Почему нельзя его просто загрузить ? При статическом подключении (явно или через pom.xml) фактически он же загружается со всеми своими потрохами.
Я не знаю что может означать "jar загружается". Есть classpath — места где ищутся классы. И это вовсе не обязательно должны быть jar (который просто обычный zip-архив). Классы могут грузится и из обычной директории, из на лету сгенерённого массива байтов и даже из сети.

В качестве аналогии можно сказать так: dll — соответсвует java-классу, а jar — это LD_LIBRARY_PATH. Вот и попробуй представь что может означать "загрузка LD_LIBRARY_PATH".
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 06.11.2024 9:47 · . Предыдущая версия .
Re[18]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 06.11.24 10:26
Оценка:
Здравствуйте, ·, Вы писали:

·>Классы в dll? Гы, такой гемор. Обычно из юзабельного только extern "C".


Да нет, ничего особенного. Вполне можно использовать.
Собственно говоря, класс есть класс независимо от того, лежит он в EXE или DLL
А как, собственно, делать иначе, если некий класс нужен в нескольких project из этого solution ? Дублировать его ?

https://learn.microsoft.com/ru-ru/cpp/cpp/using-dllimport-and-dllexport-in-cpp-classes?view=msvc-170

Ну а уж в C# в DLL только классы и есть. Там даже есть специальный тип проекта в VS — Class Library


PD>> ·>Да и вообще, ничего усложнённого я не вижу, две строчки кода. Гораздо проще, чем загружать dll — по сути указали источники, взяли класс по имени и готово.

PD>> А почему действительно нет загрузки jar ? Он же по сути аналог DLL/COM тут — набор клаcсов. Почему нельзя его просто загрузить ? При статическом подключении (явно или через pom.xml) фактически он же загружается со всеми своими потрохами.
·>Я не знаю что может означать "jar загружается". Есть classpath — места где ищутся классы. И это вовсе не обязательно должны быть jar (который просто обычный zip-архив). Классы могут грузится и из обычной директории, из на лету сгенерённого массива байтов и даже из сети.

Означать может просто "подключить все классы из этого архива". Иными словами, сделать то же самое, что делается при прописывании dependency на него в pom.xml. Только в рантайме, а не статически.
Так же как LoadLibrary означает "загрузить все, что в ней есть"

·>В качестве аналогии можно сказать так: dll — соответсвует java-классу, а jar — это LD_LIBRARY_PATH. Вот и попробуй представь что может означать "загрузка LD_LIBRARY_PATH".


Ну даже чисто формально DLL не есть один класс (или namespace), а целая коллекция средств — например, WinSock или GDI. Ты же не скажешь, что GDI32.DLL — это один класс ?
И уж в любом случае это один файл, а не path
With best regards
Pavel Dvorkin
Re[19]: JNI Attach
От: · Великобритания  
Дата: 06.11.24 11:16
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> ·>Классы в dll? Гы, такой гемор. Обычно из юзабельного только extern "C".

PD> Да нет, ничего особенного. Вполне можно использовать.
Можно, но только когда у тебя один большой проект, собирается одной версией компилятора с идентичным списком ключей. И то тебе придётся преобразовывать std::string в CString из BSTR и ещё хз чего. В случае плагинов, как у топикстартера, когда dll-и приходят из разных источников от разных вендоров, то упс.

PD> Собственно говоря, класс есть класс независимо от того, лежит он в EXE или DLL

PD> А как, собственно, делать иначе, если некий класс нужен в нескольких project из этого solution ? Дублировать его ?
Если нужен реюзабельный компонент, то оборачивать в extern "C".

PD> ·>Я не знаю что может означать "jar загружается". Есть classpath — места где ищутся классы. И это вовсе не обязательно должны быть jar (который просто обычный zip-архив). Классы могут грузится и из обычной директории, из на лету сгенерённого массива байтов и даже из сети.

PD> Означать может просто "подключить все классы из этого архива".
Что значит "подключить класс"?

PD> Иными словами, сделать то же самое, что делается при прописывании dependency на него в pom.xml. Только в рантайме, а не статически.

Ну так и с dll — всё так же. При запуске приолжения dll-ки времени сборки проекта подгружаются так же как обычно и jar-ки в pom.xml. А динамической подгрузкой надо звать LoadLibrary и GetProcAddress и мучиться с источниками. Всё то же, всё так же.

PD> Так же как LoadLibrary означает "загрузить все, что в ней есть"

Под "загрузить" ты, наверное, имеешь в виду "вызвать DllMain". Потом всё равно придётся выковыривать части по строковым именам используя GetProcAddress.
LoadLibrary — соответсвует new ClassLoader, а GetProcAddress — loadClass().

PD> ·>В качестве аналогии можно сказать так: dll — соответсвует java-классу, а jar — это LD_LIBRARY_PATH. Вот и попробуй представь что может означать "загрузка LD_LIBRARY_PATH".

PD> Ну даже чисто формально DLL не есть один класс (или namespace), а целая коллекция средств — например, WinSock или GDI. Ты же не скажешь, что GDI32.DLL — это один класс ?
PD> И уж в любом случае это один файл, а не path
Ну я же говорю, что это аналогия, а не точное соответствие.

ClassLoader позволяет хитрые манипуляции, полностью контролируя загрузку каждого класса. Примерно как допустим ты дёргаешь функцию в GDI32, а это что-то где-то внутре хочет дёрнуть другую функцию, а твой кастомный ClassLoader может взять его из другого места или даже пропатчить байткод на лету.
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[20]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 06.11.24 11:54
Оценка:
Здравствуйте, ·, Вы писали:

PD>> ·>Классы в dll? Гы, такой гемор. Обычно из юзабельного только extern "C".

PD>> Да нет, ничего особенного. Вполне можно использовать.
·>Можно, но только когда у тебя один большой проект, собирается одной версией компилятора с идентичным списком ключей. И то тебе придётся преобразовывать std::string в CString из BSTR и ещё хз чего. В случае плагинов, как у топикстартера, когда dll-и приходят из разных источников от разных вендоров, то упс.

Ну это проблемы ТС. Он о том, что это за плугины и даже о том, сам они их пишет или нет, ничего не говорил, да и не надо было.
А так да, одной версий компилятора. Впрочем, если в проекте окажется просто DLL, сделанная другой версией компилятора, то проблем может быть тоже достаточно даже без классов в DLL.

PD>> Собственно говоря, класс есть класс независимо от того, лежит он в EXE или DLL

PD>> А как, собственно, делать иначе, если некий класс нужен в нескольких project из этого solution ? Дублировать его ?
·>Если нужен реюзабельный компонент, то оборачивать в extern "C".

Ну это порой легче сказать, чем сделать. Есть какая-то иерархия классов, с виртуальными методами, попробуй ее обернуть в extern "C". Я не говорю, что вообще невозможно, но проблем будет намного больше. А потом однажды к этой иерархии добавится еще один наследник и станет совсем хорошо. А если этот наследник понадобится почему-то разместить в другой DLL или в EXE, то станет очень весело

Ну и опять же — C++ все-таки legacy язык, в него классы были добавлены, когда его из C сделали. А вот в C# прекрасно импортируются классы, потому что кроме классов, там ничего и нет.


PD>> ·>Я не знаю что может означать "jar загружается". Есть classpath — места где ищутся классы. И это вовсе не обязательно должны быть jar (который просто обычный zip-архив). Классы могут грузится и из обычной директории, из на лету сгенерённого массива байтов и даже из сети.

PD>> Означать может просто "подключить все классы из этого архива".
·>Что значит "подключить класс"?

Так в следующей строчке объяснение, оно теперь чуть ниже. Выделю.

PD>> Иными словами, сделать то же самое, что делается при прописывании dependency на него в pom.xml. Только в рантайме, а не статически.
·>Ну так и с dll — всё так же. При запуске приолжения dll-ки времени сборки проекта подгружаются так же как обычно и jar-ки в pom.xml. А динамической подгрузкой надо звать LoadLibrary и GetProcAddress и мучиться с источниками. Всё то же, всё так же.

Динамическая загрузка DLL мало чем отличается от статической. То же проецирование DLL в АП процесса, то же разрешение импортов. Только делается в рантайме, а не при запуске. А GetProcAddress к LoadLibary отношения не имеет, это потом. Я спокойно могу сделать DLL с одними ресурсами и/или внутренним кодом (в DLLMain), в которой не будет ни одного экспорта.

PD>> Так же как LoadLibrary означает "загрузить все, что в ней есть"

·>Под "загрузить" ты, наверное, имеешь в виду "вызвать DllMain". Потом всё равно придётся выковыривать части по строковым именам используя GetProcAddress.

Нет, не только. Сначала маппируется DLL на АП процесса, а потом действительно вызов DLLMain. Собственно, вызов DLLMain к загрузке отношения не имеет. Она будет вызываться и при загрузке, и при запуске/удалении потока. Это просто некое место для настройки одному из 4 событий.

·>LoadLibrary — соответсвует new ClassLoader, а GetProcAddress — loadClass().


new ClassLoader может загрузить все классы из этого jar ? Если да — то нет вопроса. Но, как я понимаю, нет ?

И вообще, в чем, собственно, концептуальная разница между DLL в .net и jar в Java ? ИМХО ее практически нет. И там и тут набор доступных извне классов. Ну а то, что там их упаковали в MZ/PE-файл, а тут в zip-файл — это дело вкуса. Смотреть что есть в этом jar/zip, конечно, проще, но и содержимое DLL посмотреть тоже не очень сложно.

PD>> ·>В качестве аналогии можно сказать так: dll — соответсвует java-классу, а jar — это LD_LIBRARY_PATH. Вот и попробуй представь что может означать "загрузка LD_LIBRARY_PATH".

PD>> Ну даже чисто формально DLL не есть один класс (или namespace), а целая коллекция средств — например, WinSock или GDI. Ты же не скажешь, что GDI32.DLL — это один класс ?
PD>> И уж в любом случае это один файл, а не path
·>Ну я же говорю, что это аналогия, а не точное соответствие.

·>ClassLoader позволяет хитрые манипуляции, полностью контролируя загрузку каждого класса. Примерно как допустим ты дёргаешь функцию в GDI32, а это что-то где-то внутре хочет дёрнуть другую функцию, а твой кастомный ClassLoader может взять его из другого места или даже пропатчить байткод на лету.


Да бога ради. LoadLibrary может взять из любого места. Загрузка DLL из памяти тоже возможна.
With best regards
Pavel Dvorkin
Re[21]: JNI Attach
От: · Великобритания  
Дата: 06.11.24 12:34
Оценка:
Здравствуйте, Pavel Dvorkin, Вы писали:

PD> то проблем может быть тоже достаточно даже без классов в DLL.

...
PD> Ну это порой легче сказать, чем сделать. Есть какая-то иерархия классов, с виртуальными методами, попробуй ее обернуть в extern "C". Я не говорю, что вообще невозможно, но проблем будет намного больше. А потом однажды к этой иерархии добавится еще один наследник и станет совсем хорошо. А если этот наследник понадобится почему-то разместить в другой DLL или в EXE, то станет очень весело
Именно, поэтому твоё "Не знаю, почему в java столь усложнили динамическую загрузку кода.". Ответ очевидный: "ничего не усложнили, всё очень просто".

PD> Ну и опять же — C++ все-таки legacy язык, в него классы были добавлены, когда его из C сделали. А вот в C# прекрасно импортируются классы, потому что кроме классов, там ничего и нет.

Ну а как думаешь с кого c# содрал все идеи? Правда хреновенько содрал...

PD> ·>Что значит "подключить класс"?

PD> Так в следующей строчке объяснение, оно теперь чуть ниже. Выделю.
Ну так не класс подключается же, а просто classpath прописывается, аналог LD_PATH. Я тебе который раз объясняю.

PD> Динамическая загрузка DLL мало чем отличается от статической.

В java вообще ничем не отличается. Если посмотришь устройство "java.exe" он просто создаёт самый обыкновенный class loader, ищет в нём указанный класс и дёргает у него метод по имени "main".

PD> То же проецирование DLL в АП процесса, то же разрешение импортов. Только делается в рантайме, а не при запуске. А GetProcAddress к LoadLibary отношения не имеет, это потом. Я спокойно могу сделать DLL с одними ресурсами и/или внутренним кодом (в DLLMain), в которой не будет ни одного экспорта.

Ну так и loadClass можешь не дёргать, а брать ресурсы через getResource.

PD> ·>Под "загрузить" ты, наверное, имеешь в виду "вызвать DllMain". Потом всё равно придётся выковыривать части по строковым именам используя GetProcAddress.

PD> Нет, не только. Сначала маппируется DLL на АП процесса, а потом действительно вызов DLLMain.
Это уже невидимая кухня как конкретно реализован LoadLibary унутре.

PD> Собственно, вызов DLLMain к загрузке отношения не имеет.

У тебя этот параграф напрямую противоречит следующему:
PD> Она будет вызываться и при загрузке, и при запуске/удалении потока. Это просто некое место для настройки одному из 4 событий.
Именно. Вот теперь объясни что же по-твоему должна делать эта твоя "загрузка jar".

PD> ·>LoadLibrary — соответсвует new ClassLoader, а GetProcAddress — loadClass().

PD> new ClassLoader может загрузить все классы из этого jar ? Если да — то нет вопроса. Но, как я понимаю, нет ?
ЕЩЁ РАЗ. Что значит "загрузить все классы"? Это бессмысленный вопрос.
Когда зовёшь loadClass напрямую (аналог GetProcAddress) ты достаёшь некий известный ожидаемый класс из заданных путей для поиска. Потом его можешь инстанциировать. Вот в этот момент VM смотрит что для инстанциации нужно. Напрмер, она рекурсивно дёргает loadClass для родительских классов, для типов параметров методов, вызывает статические инициализаторы для глобальных переменных и т.д, и т.п.

PD> И вообще, в чем, собственно, концептуальная разница между DLL в .net и jar в Java ? ИМХО ее практически нет.

DLL в .net это индуские перепевы jar в Java. Речь первоначально шла о C++.

PD> ·>ClassLoader позволяет хитрые манипуляции, полностью контролируя загрузку каждого класса. Примерно как допустим ты дёргаешь функцию в GDI32, а это что-то где-то внутре хочет дёрнуть другую функцию, а твой кастомный ClassLoader может взять его из другого места или даже пропатчить байткод на лету.

PD> Да бога ради. LoadLibrary может взять из любого места.


Ты не понял. Допустим в GDI32 у тебя есть
void func1() {func2();}
void func2() {print "hello";}

И ты делаешь
lib = LoadLibrary("GDI32");
f = GetProcAddr("func1");
f();//напишет "hello"

так вот ClassLoader позволяет резолвить имена как угодно. "func1" возьмётся из GDI32, а "func2" подставится из GDI33 и с пропатченным байт-кодом.


PD> Загрузка DLL из памяти тоже возможна.

Из RAM Drive? Шутишь?
avalon/3.0.0
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[22]: JNI Attach
От: Pavel Dvorkin Россия  
Дата: 06.11.24 13:08
Оценка:
Здравствуйте, ·, Вы писали:

Ладно, пора заканчивать. Мы уже и так далеко от проблемы ТС ушли, да и вообще скатились в офтопик.

Спасибо за дискуссию.
With best regards
Pavel Dvorkin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.