Как сделать динамически загружаемый плагин осведомленным о веб-приложении
Я решил реализовать динамическую загрузку классов в моем веб-приложении Glassfish, чтобы испытать его и поддерживать небольшие плагины, которые могут загружаться и выполняться веб-приложением во время выполнения.
Я добавил следующий класс:
public class PluginManager {
private static final String dropBoxDir = "file:///path/to/dropbox/";
private static final URLClassLoader dropBoxClassLoader;
static {
try {
URL dropBoxURL = new URL(dropBoxDir);
dropBoxClassLoader = URLClassLoader.newInstance(new URL[]{dropBoxURL});
}
catch (MalformedURLException mue) {
throw new RuntimeException("MalformedURLException thrown during PluginManager initialization - the hardcoded URL " + dropBoxDir + " must be invalid.", mue);
}
}
//this method is called by a web service
public static void runPluginFromDropBox(String fullClassName) {
try {
//load the plugin class
Class<?> pluginClass = dropBoxClassLoader.loadClass(fullClassName);
//instantiate it
Runnable plugin = (Runnable)pluginClass.newInstance();
//call its run() method
plugin.run();
}
catch (ClassNotFoundException cnfe) {
throw new RuntimeException("The class file for " + fullClassName + " could not be located at the designated directory (" + dropBoxDir + "). Check that the specified class name is correct, and that its file is in the right location.", cnfe);
}
catch (InstantiationException ie) {
throw new RuntimeException("InstantiationException thrown when attempting to instantiate the plugin class " + fullClassName + " - make sure it is an instantiable class with a no-arg constructor.", ie);
}
catch (IllegalAccessException iae) {
throw new RuntimeException("IllegalAccessException thrown when attempting to instantiate the plugin class " + fullClassName + " - make sure the class and its no-arg constructor have public access.", iae);
}
catch (ClassCastException cce) {
throw new RuntimeException("Plugin instance could not be cast to Runnable - plugin classes must implement this interface.", cce);
}
}
}
Затем в отдельном проекте я создал тестовый плагин:
public class TestPlugin implements Runnable {
@Override
public void run() {
System.out.println("plugin code executed");
}
}
Я развернул веб-приложение, затем скомпилировал TestPlugin
в .class
файл и поместил его в назначенную папку. Я позвонил в веб-сервис, который ударил runPluginFromDropBox()
с именем класса и получил ожидаемый результат.
Все это работало как подтверждение концепции, но мой плагин практически бесполезен, если его не узнать о классах моего веб-приложения. С тех пор я прочитал это .war
предназначено только как отдельное приложение и не предназначено для пути к классам других библиотек, что не сулит ничего хорошего для этого небольшого побочного проекта.
Я взглянул на эту дискуссию: расширение веб-приложений Java с помощью плагинов и ощущение, что я без особых на то оснований сталкиваюсь с множеством проблем проектирования и должен обернуться. Однако этот пост довольно старый и специфичен для Tomcat, поэтому я просто подумал, что могу спросить, есть ли какой-нибудь прямой способ подойти к этому без какой-либо сложной сторонней структуры.
1 ответ
Классы файла войны загружаются определенным загрузчиком классов, чтобы изолировать войну от других веб-приложений, развернутых на том же сервере, и иметь возможность отменить развертывание войны.
Чтобы знать о классах webapp, ваш загрузчик классов плагина должен иметь загрузчик классов webapp в качестве родителя. Я не знаю всех проблем, которые могут возникнуть при использовании дополнительного загрузчика классов, но я подозреваю, что у вас могут возникнуть утечки памяти и другие неприятные проблемы (статические значения, сохраняемые в памяти и т. Д.), Когда контейнер будет развернут и повторно развернут веб-приложение. И у вас могут быть различия между контейнерами.