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

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

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

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

Ответ 1

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

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

Архитектура

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

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

enter image description here

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

enter image description here

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

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

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

Вот несколько деталей:

Ответ 2

В основном вы хотите реализовать extensibility или шаблон дизайна плагина. Существует несколько способов реализации этого сценария.

Какой бы компонент, который вы хотите разрешить кому-либо другому, перезагрузить модуль, определить интерфейс и реализовать свою собственную реализацию по умолчанию. Например, здесь я пытаюсь предоставить 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, пожалуйста, посмотрите. Счастливое кодирование!

Ответ 3

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

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