Пользовательский Tomcat Webapp ClassLoader
Я пытаюсь реализовать пользовательский загрузчик классов для Tomcat. Моя первая попытка привела к исключению приведения класса (очевидно, tomcat пытается привести мой загрузчик к org.apache.catalina.loader.WebappLoader). Хорошо, я расширил WebappLoader и добавил catalina.jar в мой путь сборки.
Теперь я готов к развертыванию (я думаю). Я получаю эту ошибку:
SEVERE: Catalina.start: LifecycleException: start:: java.lang.NoClassDefFoundError: org / apache / catalina / loader / WebappLoader
Tomcat поставляется с catalina.jar для запуска, поэтому я на 99,9% уверен, что он уже загружен в Tomcat. Я проверил это, проверив /server/lib/catalina.jar, который содержит apache WebappLoader. Кроме того, ручное связывание другого файла catalina.jar создает целый беспорядок, как и ожидалось.
Я не совсем понимаю. Любые советы будут горячими.
Спасибо!
Обновление: Интересно, что то же самое на tomcat6 (расширение WebappLoader; работает на tomcat5.5) все еще вызывает исключение ClassCastException. Похоже, что класс, выполняющий приведение, был загружен с использованием загрузчика, отличного от того, который загружал мой класс. Во всяком случае, я не понимаю, как бы я мог контролировать это, разве что где-то еще может отсутствовать конфигурация tomcat? Любые идеи для Tomcat6 тоже?
2 ответа
Может быть, я плотный, но я думаю, что это должен быть WebappClassLoader, а не WebappLoader. Импорт выглядит нормально, хотя.
Не зная специфики вашего кода и настроек, невозможно быть уверенным, но это напоминает мне о проблеме, с которой я столкнулся, когда попробовал свои силы в пользовательских загрузчиках классов.
Сценарий таков:
- Загрузчик (собственно JVM /Tomcat/ что угодно) загружает ваш код
- Ваш загрузчик классов загружает ваши дополнения, недоступные в classpath вышеупомянутого.
- Ваш код ссылается на эти дополнения.
- Эти дополнения не доступны в том же пространстве имен, что и код, загруженный загрузчиком.
- Ваш код в пространстве имен загрузчика запускается, пытается ссылаться на код в вашем пользовательском пространстве имен, и это не видно из пространства имен загрузчика. Таким образом, в этот момент JVM выдает ошибку NoClassDefFound.
Причина этого заключается в том, что иерархия загрузчиков классов работает только в одном направлении: то есть код из подпространств имен (дочерние загрузчики классов) недоступен (видим) в более широком родительском пространстве имен (родительский загрузчик классов); и нет хорошего способа взломать эту систему.
К счастью, вы можете определить интерфейсы, которые доступны в родительском пространстве имен, а затем реализовать их в коде, видимом только во вложенном пространстве имен. Затем код, который находится только в родительском пространстве имен, может использовать это имя интерфейса для целей приведения и вызова метода и, таким образом, получить доступ к вашему компоненту подпространства имен независимо.
Чтобы это работало, вы должны убедиться, что ваш пользовательский загрузчик классов не только загружает компоненты, к которым родительский загрузчик не имеет доступа (то есть находится за пределами его пути к классам), но также и те биты вашего кода, которые напрямую взаимодействуют с этими компонентами и явно обозначьте эти символы (имена / методы и т. д.). В противном случае ссылки на эти компоненты оказываются в родительском пространстве имен (помните, загрузчик загрузчика классов по умолчанию загружает весь ваш собственный код), и вы возвращаетесь к исходной проблеме.
Вы можете сделать это, подавив предполагаемую модель делегирования загрузки классов. Как правило, вы должны отложить загрузчик родительский, прежде чем пытаться загрузить классы самостоятельно. Однако теперь вы должны заранее проверить, что ваш код не касается ни одного из этих компонентов, недоступных для родительского загрузчика. Возможно, самый простой способ - настроить путь к коду таким образом, чтобы загрузчик классов поддерживал набор имен классов, которые он должен загружать сам, а не позволять родительскому загрузчику загружать их.
Вы должны найти способ как-то сказать своему пользовательскому загрузчику классов, для этого можно использовать аннотацию типа в объявлениях классов. Идея заключается в том, что ваш загрузчик классов анализирует классы, загруженные родителем, и, если он находит конкретную пользовательскую аннотацию для имени типа, он будет вызывать методы для аннотации, чтобы получить имя символов класса, которые он не должен разрешать своему родительскому загрузчику. нагрузки.
Пример:
@MyCustomAnnotation(implementation="my.custom.package.MyImpl")
public class MyFeatureProvider {
public MyFeature getFeature() { // return an instance of MyImpl here }
}
Обратите внимание, потому что класс MyFeatureProvider
будет загружен раньше MyImpl
ваш классный загрузчик заранее узнает об аннотации в MyFeatureProvider
поэтому он сможет переопределить модель делегирования по умолчанию для MyImpl. Потому что остальная часть вашего кода взаимодействует только с MyImpl
как пример MyFeature
родительский загрузчик никогда не должен блокироваться при виде неопределенных символов - и ошибка ClassNoDefFound решена.