Клиент удаленного выполнения возможен с помощью ASM?

Я хочу создать собственный клиент удаленного выполнения для своего приложения. Клиент может выглядеть примерно так:

interface Client {

    <T> T computeRemotely(Function<List<MyBigObject>, T> consumer)
}

и может использоваться следующим образом:

Client client = new Client();
Integer remoteResult = client.computeRemotely(list -> {
    Integer result = -1;

    // do some computational work here.

    return result;
});

Это означает, что мне каким-то образом нужно взять лямбда-выражение от клиента, отправить его на сервер, запустить функцию (передав реальный List<MyBigObject>) и отправить результат обратно.

Стоит отметить, что ограничение на использование моей клиентской библиотеки заключается в том, что вы не можете использовать что-либо вне JDK в этой лямбде и ожидать, что она будет работать (поскольку классы могут не находиться в пути к классам на сервере)... но я бы хотел, чтобы они чтобы иметь возможность использовать любой из классов JDK для внесения собственных данных в расчет.

Теперь я не могу просто сериализовать лямбду Function<MyBigObject, T>, потому что она сериализуется как внутренний клиент любого класса, в котором существует лямбда, который НЕ будет находиться в пути к классам на сервере.

Поэтому я смотрел на ASM, чтобы увидеть, может ли это работать. Учитывая, что я никогда раньше не занимался манипуляциями с байт-кодом, я просто хотел проверить, правильно ли я говорю:

  • Я могу использовать ASM для преобразования для чтения класса, в котором находится лямбда.
  • Используя посетитель метода, получите байты метода, отправьте их на сервер
  • Используйте ASM, чтобы создать экземпляр из байтов и выполнить его.

Учитывая, что лямбда похожа на анонимный внутренний класс, я предполагаю, что мне тоже придется сделать какое-то переименование метода.

Это примерно правильно или я совсем не туда попал?


person Cheetah    schedule 17.03.2015    source источник
comment
Я не знаком с реализацией лямбда, поэтому не уверен, что нужно какое-либо переименование. Но если он есть, то нужно переименовывать везде - в частности в байткоде методов - любой доступ к полям и методам переименованного класса. Возможно, не так уж сложно, но хорошо иметь в виду. Существуют классы Remapper и org.objectweb.asm.commons.RemappingClassAdapter, которые, кажется, могут обрабатывать переименование.   -  person marek.jancuska    schedule 17.03.2015


Ответы (2)


Обратите внимание, что лямбда-выражения могут получить доступ ко всем неизменяемым значениям из своего контекста. Таким образом, вам нужно либо запретить доступ к внешним значениям (что серьезно ограничит полезность вашего решения), либо идентифицировать их и отправить представления этих значений (что приводит к упомянутой вами проблеме; их реализация может быть вне пути к классам сервера) .

Таким образом, даже если вы отправите представление метода (для которого вам даже не понадобится ASM, вы можете получить Resource непосредственно из загрузчика классов), в общем случае это не сработает.

Изменить: учитывая ваш комментарий, это может сработать. Вам нужно будет синтезировать класс с

  • Атрибуты контекста в качестве конечных полей
  • Конструктор с аргументами для всех полей (вы будете передавать туда десериализованные значения при построении)
  • Лямбда-метод — подробности см. в этом вопросе.
person llogiq    schedule 17.03.2015
comment
Я планировал разрешить доступ к любым неизменяемым значениям, которые были частью JDK (например, спискам, коллекциям и т. д.), но не к пользовательским классам. - person Cheetah; 17.03.2015

Нет смысла анализировать сгенерированные во время выполнения классы лямбда-выражений. У них очень простая структура, которую они раскрывают при сериализации. Во время сериализации они будут заменены SerializedLambda экземпляр, который содержит всю информацию, которую вы когда-либо могли собрать, в частности:

Важным моментом является целевой метод. Для лямбда-выражений (в отличие от ссылок на методы) целевой метод является синтетическим методом, который находится в классе, содержащем лямбда-выражение. Кстати, это метод private, поэтому попытка репликации класса, вызывающего этот метод, обречена на провал, для создания такого класса требуется специальное взаимодействие с JVM. Но важным моментом является то, что вам нужен целевой метод на удаленной стороне для его выполнения, конкретный лямбда-класс среды выполнения не имеет значения. Если у вас есть целевой метод, вы можете создать экземпляр lambda без сторонних библиотек.

Конечно, вы можете использовать ASM для анализа кода целевого метода, чтобы передать его (и все зависимости) на удаленную сторону, но обратите внимание, что это ничем не отличается от передачи произвольных Function реализаций; тот факт, что существует слой сгенерированного во время выполнения класса, созданного с помощью лямбда-выражения, никак не поможет вам.

person Holger    schedule 18.03.2015