Как интегрировать beanshell

Я разрабатываю игровой движок на основе компонентов в java, прямо сейчас, когда у меня есть изменения в компонентах, мне нужно перестроить и перезапустить редактор, чтобы изменения вступили в силу (или я могу использовать некоторую ограниченную горячую инъекцию кода, если приложение работает в режим отладки).

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

Можете ли вы дать мне какие-либо указания о том, как интегрировать интерпретатор beanshell в проект? Я могу вручную отслеживать исходную папку на наличие изменений и передавать в нее обновленные классы Java, но как на самом деле будет происходить горячая замена?


person Anton Banchev    schedule 06.12.2014    source источник
comment
beanshell.org/manual/embeddedmode.html   -  person Pratik Butani    schedule 09.12.2014
comment
Интересно, никто не упомянул OSGi. en.wikipedia.org/wiki/OSGi   -  person Yser    schedule 15.12.2014


Ответы (3)


Во-первых, название немного сбивает с толку. Вам не нужно интегрировать BeanShell. Что вам на самом деле нужно:

  1. определить правильную архитектуру
  2. использовать Java Compiler API для работы с классами Java

Архитектура

Допустим, у вас есть граф объектов. Есть много объектов, ссылок и т. д. Так что будет очень сложной задачей заменить какой-то экземпляр новым. Вместо решения этой проблемы вы можете скрыть динамическую часть за «статическим» прокси. Прокси будет обрабатывать всю перезагрузку (включая мониторинг исходной папки).

Перед перезагрузкой:

введите здесь описание изображения

После перезагрузки:

введите здесь описание изображения

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

API компилятора Java

Вместо использования интерпретируемых языков вы можете использовать Java, компилируя его на лету и загружая с помощью 'Class.forName()'. Есть много разных примеров из-за того, что этот подход существовал какое-то время.

Вот некоторые детали:

person Renat Gilmanov    schedule 09.12.2014
comment
Мой вопрос был очень конкретным, как интегрировать beanshell для горячей замены, но на него никто не ответил. Я не хочу вручную перекомпилировать код и вручную управлять динамической перезагрузкой, также стоит отметить, что использование прокси-сервера между динамической и статической частями нецелесообразно, поскольку это приведет к дополнительным накладным расходам на конечных устройствах (включая некоторые старые устройства j2me). - person Anton Banchev; 16.12.2014
comment
Ваш ответ выглядит наиболее полным для других людей с похожими проблемами, поэтому я выбираю его для награды. - person Anton Banchev; 16.12.2014

По сути, вы хотите реализовать расширяемость или шаблон дизайна плагина. Есть несколько способов реализовать этот сценарий.

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

 public interface HelloInterface {

    public String sayHello(String input);
    ..   
 }

 public class HelloImplDefault implements HelloInterface {

    public String sayHello(String input) {
         return "Hello World";
    }
 }

Теперь разрешите пользователю добавлять файлы плагина (пользовательской реализации) по предварительно настроенному пути. Вы можете использовать FileSystemWatcher или фоновый поток для сканирования этого пути и попытаться скомпилировать и загрузить файл.

Чтобы скомпилировать java-файл,

    private void  compile(List<File> files) throws IOException{


        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        DiagnosticCollector<JavaFileObject> diagnostics = new DiagnosticCollector<JavaFileObject>();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(diagnostics, null, null);
        Iterable<? extends JavaFileObject> compilationUnits = fileManager
            .getJavaFileObjectsFromFiles(files);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, diagnostics, null,
            null, compilationUnits);
        boolean success = task.call();
        fileManager.close();

}

Чтобы загрузить файл класса,

private void load(List<File> files) throws MalformedURLException, InstantiationException, IllegalAccessException, ClassNotFoundException{

     ClassLoader cl = Thread.currentThread().getContextClassLoader();

    for(File f: files){
        if(f.getName().endsWith(".class") && !loadedClass.contains(f.getName())){
             URL url = f.toURL();
             URL[] urls = new URL[]{url};
             Object obj = cl.loadClass(f.getName().replace(".class", "")).newInstance();
             if(obj instanceof HelloInterface){
                 HelloProviders.addProvider((HelloInterface)obj);
                 System.out.println("Loaded "+ ((HelloInterface)obj).getProviderName());
             }else{
                 //Add more classes if you want
             }
             loadedClass.add(f.getName());
        }
    }
}

На этом этапе вы можете прочитать пользовательскую реализацию и загрузить ее в загрузчик системных классов. Теперь вы готовы идти. У этого подхода есть последствия для безопасности, которые вам нужно узнать из Интернета.

Я реализовал один пример кода и разместил его на github, пожалуйста, посмотрите. Удачного кодирования!

person kamoor    schedule 14.12.2014

Взгляните на tapestry-ioc инверсию контейнера управления, который поддерживает перезагрузка в реальном времени.

В режиме разработки (tapestry.production-mode=false) вы можете перезагрузить свои сервисы в реальном времени. Обратите внимание, что если интерфейс службы изменится, вам потребуется перезагрузка. Но любые изменения в реализации службы, которые не изменяют интерфейс службы, могут быть перезагружены в режиме реального времени.

person lance-java    schedule 15.12.2014