Tomcat обновляет Java-файл во время выполнения - PullRequest
0 голосов
/ 27 мая 2018

Я строю динамический веб-проект (в Eclipse с Tomcat в качестве сервера) с использованием сервлетов и JSP.Общая цель проекта - позволить пользователю решать небольшие проблемы с кодом.Для этого я беру метод, написанный пользователем, и использую его для создания Java-файла, который я могу запустить через Reflection .Проблема, которую я не могу понять, состоит в том, что Tomcat (или Eclipse?) Не обновляет файл во время выполнения.Поэтому, когда я создаю файл, используя код текущего пользователя, и пытаюсь скомпилировать его, моя программа всегда выполняет файл, как это было, когда я запускал сервер, используя код предыдущего пользователя.Как я могу сказать ему обновить файл перед запуском?

Редактировать: Вот как я создаю файл:

public boolean writeFile() {
    try {
        PrintWriter writer = new PrintWriter(relativePath + "src\\testfiles\\TestFile.java");
        writer.print(content);
        writer.close();
        return true; }...

Здесь я вызываю писателя и пытаюсьзапустив файл:

FileWriter writer = new FileWriter(content);        
    if(writer.writeFile()){
        Class<?> TestFile;
        Method m;
        try {
            TestFile = cl.loadClass("testfiles.TestFile");
            m = TestFile.getDeclaredMethod("testSolution");
            m.invoke(null);

Заранее спасибо!

1 Ответ

0 голосов
/ 27 мая 2018

Хорошо, теперь понятно, в чем проблема.Ваша проблема не в том, что Tomcat не перезагружает файл, а в том, что загрузчик классов не перезагружает класс.

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

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

Теоретически вы можете просто каждый раз иметь новый класс (изменяя его имя при каждом сохранении), но вы 'В конечном итоге у меня заканчивается память.

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

ОБНОВЛЕНИЕ: Я действовал в предположении, что вы уже знаете, как скомпилироватькласс во время выполнения, но, основываясь на комментариях, это не так.Загрузчик классов может, конечно, загружать только скомпилированный класс, поэтому исходный код не очень полезен.

Внутренне Java предоставляет интерфейс компилятора под javax.tools.JavaCompiler, но его нелегко использовать и требует другой обработкиверсий Java до и после Java 9. Поэтому гораздо проще использовать библиотеку типа jOOR , которая скрывает кровавые части:

Class clazz = Reflect.compile("com.example.Test",
                "package com.example;" +
                "public class Test {\n" +
                "        public String hello() {\n" +
                "            return \"hello\";\n" +
                "        }\n" +
                "    }")
              .type();

Вместо type(), чтобы просто получитькласс, вы на самом деле можете продолжать использовать API беглого отражения jOOR для запуска методов сгенерированного класса или того, что вы обычно делаете с помощью регулярного отражения.

Для прямого использования JavaCompiler см., например, это или, что еще лучше, исходный код jOOR.

...