У меня есть веб-сервис, который мы будем называть service.war. Он реализует интерфейс, который мы будем называть ServicePluginInterface. Во время запуска service.war он читает переменные окружения и использует их для поиска банки (MyPlugin.jar). Когда он находит эту банку, она использует вторую переменную среды для загрузки плагина внутри банки. Класс, который он загружает выглядит следующим образом:Как реализовать общий интерфейс, извлеченный из WAR
public class MyPlugin implements ServicePluginInterface {...}
сервлет пытается загрузить плагин с помощью кода, как:
try {
if (pluginClass == null) {
plugin = null;
}
else {
ZipClassLoader zipLoader = new ZipClassLoader(Main.class.getClassLoader(), pluginJar);
plugin = (ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance();
plugin.getAccount(null,null);
}
} catch (Exception e) {
...
}
Хитрость заключается в том, что у меня нет источника или баночку для ServicePluginInterface. Не желая сдаваться так легко, я вытащил файлы классов из файлов service.war. Используя эти файлы классов в качестве зависимостей, я смог создать без предупреждений компилятора MyPlugin. Однако, когда на самом деле выполнены Tomcat, секция кода выше генерирует исключение во время выполнения:
java.lang.ClassCastException: com.whatever.MyPlugin cannot be cast to com.whomever.ServicePluginInterface
В качестве второй точки отсчета, я также удалось построить синтетическую загрузчик классов (отдельный Java исполняемый файл, который использует тот же механизм загрузки класса. Опять же, поскольку у меня нет исходного источника для ServicePluginInterface, я использовал файлы классов из WAR. Этот второй, синтетический загрузчик или faux-сервлет, если хотите, МОЖЕТ загружать MyPlugin просто отлично. Я бы постулировал, что JVM Tomcat, похоже, обнаруживает какую-то разницу между классами, найденными внутри WAR, и извлеченными файлами класса. Однако, поскольку все, что я делал для извлечения файлов классов, было открыть WAR как zip d скопируйте их, трудно себе представить, что это может быть.
Хавьер сделал полезное предложение об удалении определения ServicePluginInterface, проблема с этим решением было то, что ZipClassLoader что сервлет использует для загрузки плагина из кувшина переопределяет функцию ClassLoader findClass, чтобы вытащить класс из ЕАО, как так:
protected Class<?> findClass(String name) throws ClassNotFoundException
{
ZipEntry entry = this.myFile.getEntry(name.replace('.', '/') + ".class");
if (entry == null) {
throw new ClassNotFoundException(name);
}
...
}
класс ZipClassLoader затем рекурсивно загружает все родительские объекты и интерфейсы из кувшина. Это означает, что если в плагин-банке не содержится определение для ServicePluginInterface, он будет терпеть неудачу.
Почему ZipClassLoader не делегирует его родителям? Какая реализация? – Javier
Ну, к сожалению, я думаю, что ZipClassLoader защищен авторским правом владельца сервлета, поэтому я не могу поделиться им полностью, но теперь мне интересно, можно ли написать лучший ZipClassLoader и заменить файл класса в WAR. .. – OverclockedTim