Статьи

RelProxy: Reloader и создание сценариев для Java и Groovy

Что такое  RelProxy ?

RelProxy имеет три основных функции:

1) Перегрузчик классов во время выполнения из исходного кода на Groovy или Java. Похоже на JRebel, но не так сложно … и дешевле.

2) Среда сценариев для Java в качестве языка сценариев, включая «сценарии оболочки», написанные на чистой Java.

3) Поддержка Java Scripting API JSR-223 для «языка сценариев с именем Java».

RelProxy был создан для автоматической перезагрузки классов в  ItsNat , веб-инфраструктуру на основе Java, для кода на Groovy, он стал инструментом общего назначения, включая поддержку Java.

1) Перегрузчик классов во время выполнения из исходного кода на Groovy или Java.


Пойдем сначала с Groovy.

Вы знаете, Groovy — это динамический язык, который поддерживает встроенную перезагрузку классов.
Может быть, вы не знаете, Groovy это язык на основе байт-кода. В этом случае Groovy движок компилирует классы Groovy по требованию.

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

Это когда RelProxy делает свою работу через утилиту под названием GProxy. С помощью GProxy мы создаем java.lang.reflect.Proxy и передаем этот прокси вместо исходного объекта Groovy. Когда метод вызывается в java.lang.reflect.Proxy, GProxy перехватывает вызов метода через Groovy, механизм RelProxy проверяет, изменился ли какой-либо класс, когда изменился исходный код класса исходного объекта или другого зависимого класса, исходный объект воссоздается на основе нового класса, тех же зависимых классов.


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

То же самое применяется к коду Java с помощью утилиты JProxy, в данном случае это RelProxy, который своевременно проверяет изменения исходного кода, компилирует измененные классы на лету и перезагружает все классы с новым ClassLoader.
Ниже приведен пример Java на основе ItsNat:

FalseDB db = new FalseDB();
ItsNatServletRequestListener listener = 
   JProxy.create(new example.javaex.JProxyExampleLoadListener(db), 
      ItsNatServletRequestListener.class);
docTemplate.addItsNatServletRequestListener(listener);

Объект JProxyExampleLoadListener является перезагружаемым, те же самые его зависимости, но FalseDB, потому что создан «вне среды прокси».
В случае веб-проекта
ItsNat
класс  JProxyExampleLoadListener (реализующий  ItsNatServletRequestListener) — это место, где мы кодируем логику представления, может ли этот класс и связанные / зависимые классы быть загружены в горячем режиме, а также, если его изменения перезагружаются чистыми HTML-шаблонами ItsNat, у нас есть разработка среда (также в рабочей среде?), в которой количество повторных развертываний резко сокращается, поскольку дизайн представления и логика представления могут быть перегружены.
Пример Groovy точно такой же, но в Groovy (Groovy является альтернативным языком для проектов ItsNat).

2) Среда сценариев для Java как «язык сценариев»,  «сценарии оболочки» на основе чистой Java

Благодаря утилите jproxysh вы можете создать архив, например, с именем example_java_shell, с таким содержанием:
#!/usr/bin/env jproxysh

String msg = args[0] + args[1];
System.out.println(msg);
System.out.println("example_java_shell 1 ");
example.javashellex.JProxyShellExample.exec();

и будет выполнен без предварительной компиляции.

Это может быть полный класс Java:
#!/usr/bin/env jproxysh

import example.javashellex.JProxyShellExample;
public class example_java_shell_complete_class
{
    public static void main(String[] args)
    {
        String msg = args[0] + args[1];
        System.out.println(msg);
        System.out.println("example_java_shell_complete_class 1 ");
        JProxyShellExample.exec();
    }
}

JProxyShellExample также может быть классом Java только с исходным кодом.


Также это может быть обычный класс Java, опять же без необходимости компиляции (проекты JavaSE без компиляции):
jproxysh example_normal_class.java "HELLO " "WORLD!"

Мы можем выполнить фрагмент кода:
jproxysh -c 'System.out.print("This code snippet says: ");' \
                   'System.out.println("Hello World!!");'

Или полный класс:
jproxysh -c 'public class _jproxyMainClass_ { '  \
            ' public static void main(String[] args) { ' \
            '    System.out.print("This code snippet says: ");' \
            '    System.out.println("Hello World!!");' \
            '  }' \
            '}'
Наконец, вызывая jproxysh без параметров:

jproxysh

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

Следует отметить, что RelProxy НЕ создает новый язык, похожий на Java, как это делает BeanShell, RelProxy просто компилирует на лету в памяти (опционально может сохранять файлы .class).

3) Поддержка JSR-223 Java Scripting API для языка сценариев «Java»

Java — это еще один поддерживаемый язык сценариев:
JProxyScriptEngineFactory factory = JProxyScriptEngineFactory.create(jpConfig);

ScriptEngineManager manager = new ScriptEngineManager();
manager.registerEngineName("Java", factory);
manager.getBindings().put("msg","HELLO GLOBAL WORLD!");

ScriptEngine engine = manager.getEngineByName("Java");
Bindings bindings = engine.createBindings();
bindings.put("msg","HELLO SCOPE WORLD!");

StringBuilder code = new StringBuilder();
code.append( " javax.script.Bindings bindings = context.getBindings(javax.script.ScriptContext.ENGINE_SCOPE); \n");
code.append( " String msg = (String)bindings.get(\"msg\"); \n");
code.append( " System.out.println(msg); \n");
code.append( " bindings = context.getBindings(javax.script.ScriptContext.GLOBAL_SCOPE); \n");
code.append( " msg = (String)bindings.get(\"msg\"); \n");
code.append( " System.out.println(msg); \n");
code.append( " example.javashellex.JProxyShellExample.exec(engine); \n");
code.append( " return \"SUCCESS\";");

String result = (String)engine.eval( code.toString() , bindings);
System.out.println("RETURNED: " + result);

НАСЛАЖДАТЬСЯ!!