2013-12-18 3 views
1

У меня есть веб-сервис, который мы будем называть 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, он будет терпеть неудачу.

+0

Почему ZipClassLoader не делегирует его родителям? Какая реализация? – Javier

+0

Ну, к сожалению, я думаю, что ZipClassLoader защищен авторским правом владельца сервлета, поэтому я не могу поделиться им полностью, но теперь мне интересно, можно ли написать лучший ZipClassLoader и заменить файл класса в WAR. .. – OverclockedTim

ответ

1

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

Во время выполнения несколько ссылочных типов с тем же именем бинарным могут быть загружены одновременно различными загрузчиками классов. Эти типы могут или не могут представлять одно и то же объявление типа. Даже если два таких типа представляют однотипное объявление, они считаются отличными. JLS

В этом случае zipLoader возвращает экземпляр MyPlugin, который реализует другойServicePluginInterface (это он загружен из архива тоже?):

(ServicePluginInterface)zipLoader.loadClass(pluginClass).newInstance(); 

кажется, что сервер приложений уже имеет определение ServicePluginInterface, то вам не нужно его повторно развертывать. Достаточно добавить необходимые файлы (ServicePluginInterface и т. Д.).) как не развернутые зависимости вашего проекта.

Другой подход идет жить с тем, и доступ к методам в ServicePluginInterface посредством отражения (используйте Class объект, возвращаемый zipLoader, вместо ServicePluginInterface.class).

+0

Очень полезный комментарий о загрузчиках классов - я подозреваю, что настоящий трюк лежит где-то в этой области. К сожалению, по неизвестным вам причинам, я считаю, что это не решит мою проблему из-за характера ZipClassLoader. Я добавил детали в конец вопроса, где они могут быть хорошо отформатированы, но, кроме этого, ZipClassLoader терпит неудачу, если интерфейс также не найден в JAR. Что касается второго предложения, к сожалению, невозможно изменить код загрузчика сервлетов, поскольку он был предоставлен третьей стороной, и у меня нет исходного кода. – OverclockedTim

+0

Итак, я буду отмечать ваш ответ как принятый, поскольку это, вероятно, более общеприменимое и полезное решение. То, что я на самом деле закончил, полностью переписало файлы классов ZipClassLoader одним из моих собственных, который непосредственно загружает (без открытия JAR) класс Plugin, который, конечно же, мне также пришлось разместить внутри WAR. Уродливый взлом, но он работал ... – OverclockedTim

Смежные вопросы