Пользовательский загрузчик классов для компиляции приложения Java EE

Вот требования:

  1. Веб-приложение Java EE (в Tomcat)...
  2. Мертво просто, только JSP, сервлеты и банки - никаких фреймворков...
  3. Ничего не нужно перезагружать, ни сервер, ни контекст, ничего...

На данный момент идея состоит в том, чтобы расширить WebappClassLoader (catalina.jar), чтобы создать свой собственный загрузчик классов и зарегистрировать его в context.xml как элемент Loader. С небольшим количеством кода вы можете красиво написать свой загрузчик классов, который будет знать, где найти исходные файлы Java, а затем скомпилировать их в файлы классов, если это необходимо, а затем загрузить их в память, когда их попросят сделать это. Логика понятна и проста.

Кроме:

Как Jasper узнает, где — автоматически — найти ваши классы, сгенерированные вашим пользовательским загрузчиком классов, чтобы он мог скомпилировать JSP, которые на них ссылаются, и даже обновить их (ваши классы) на лету? Неужели невозможно достичь?

Что вы думаете?

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


person Takis Bouyouris    schedule 07.12.2011    source источник
comment
В документации Jasper указано, что: JDT использовался для компиляции страниц JSP — компилятор Eclipse JDT Java теперь используется для выполнения компиляции исходного кода JSP java. Этот компилятор загружает исходные зависимости из загрузчика классов контейнера. Ant и javac по-прежнему можно использовать. Таким образом, вопрос на самом деле касается загрузчика классов контейнера!   -  person Takis Bouyouris    schedule 08.12.2011
comment
Загрузчик классов контейнера является экземпляром org.apache.catalina.loader.StandardClassLoader. Это загрузчик классов Engine. Я не вижу очевидного способа переопределить/дополнить это для автоматической работы...   -  person Takis Bouyouris    schedule 08.12.2011


Ответы (1)


Можно добавить загрузчик классов в текущий контекст веб-приложения (context.xml):

<Context>
    <Loader loaderClass = "gr.nevma.cccl.CompilingClassLoader"/>
</Context>

А затем создайте загрузчик классов, например:

package package gr.nevma.cccl;

import ...

public class CompilingClassLoader extends WebappClassLoader {

    public CompilingClassLoader ( ClassLoader parentClassLoader ) {

        super( parentClassLoader );

    }

    public Class<?> loadClass ( String className, boolean resolve ) throws ClassNotFoundException { 

        Class<?> theClass = null;

        // Do you stuff here
        return theClass;

    }

}

Но это помогло бы только в том случае, если бы кто-то хотел иметь возможность явно загружать классы из самого загрузчика классов, вызывая ClassLoader::loadClass(...). Это не помогло бы в компиляции JSP Джаспером. Это означает, что когда Jasper попытается скомпилировать JSP, которые используют классы, которые должны были быть загружены этим загрузчиком классов, компиляция завершится ошибкой, потому что Jasper даже не попросит этот загрузчик классов вообще загрузить какие-либо классы.

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

person Takis Bouyouris    schedule 12.12.2011
comment
Похоже, что метод getResourceAsStream() из WebappClassLoader вызывается для загрузки классов, которые нужны Jasper для компиляции страниц JSP. Другими словами, когда Jasper пытается скомпилировать страницы JSP, он запрашивает необходимые классы из загрузчика классов, вызывая getResourceAsStream(). Таким образом, этот метод необходимо переопределить, чтобы динамически загружать все, что нужно загрузить... - person Takis Bouyouris; 14.12.2011
comment
Для того, чтобы иметь возможность загрузить, а затем перезагрузить любой класс (т.е. определить его), необходимо удалить и создать заново его загрузчик классов. Другими словами, экземпляр загрузчика классов может загрузить (определить) класс только один раз! Если класс необходимо фактически перезагрузить (определить снова), то для этого задания необходимо создать новый загрузчик классов, а старый удалить. - person Takis Bouyouris; 16.12.2011
comment
Когда загрузчик классов загрузил класс один раз, кажется, что JVM никогда больше не запрашивает у него этот класс, по крайней мере, до тех пор, пока память PermGen (где классы хранятся в ОЗУ) не заполнена. Таким образом, это делает неизбежным удаление загрузчика классов и его создание заново, чтобы иметь возможность перезагрузить определение класса. И это делает еще более ясным, что если ссылки на перезагруженный класс остаются висеть без дела, то экземпляр загрузчика классов, который их загрузил, останется без сборки мусора. - person Takis Bouyouris; 19.12.2011
comment
Кроме того, взглянув на исходный код Tomcat в классе JspCompilationContext в Jasper, можно увидеть, что это именно то, что делает Jasper для компиляции и перезагрузки JSP-страниц! Он периодически проверяет исходные файлы JSP на наличие изменений и, если какие-либо изменения обнаружены, уничтожает свой загрузчик классов, создает его новый экземпляр, компилирует страницы JSP, которые необходимо скомпилировать, и перезагружает их с новым экземпляром загрузчика классов. И, да, это действительно звучит как много работы для сервера, если он установлен в очень частом темпе. - person Takis Bouyouris; 19.12.2011