実行時にプラグインをロードできる Web アプリケーションに取り組んでいます。OSGi がこれに対する洗練されたソリューションであることは承知していますが、GWT が使用されているため、OSGi への移行は見られません。
プラグインを実装するために、3 つの jar があります: アプリケーション、pluginAPI :
public interface NotificationPlugin {
public String getName();
}
そして plugin.jar
public class Plugin implements NotificationPlugin {
private final String PLUGIN_NAME = "Plugin";
@Override
public String getName() {
return PLUGIN_NAME;
}
}
plugin.jar と Web アプリケーションには、pluginapi.jar への依存関係があります。アプリケーションでは、各 jar ファイルを個別の URLClassloader でロードするため、個別にアンロードできます。
service.setUcl(new URLClassLoader(url));
ServiceLoader<NotificationPlugin> sl = ServiceLoader.load(NotificationPlugin.class, service.getUcl());
Iterator<NotificationPlugin> apit = sl.iterator();
service.setPlugin(apit.next());
while (apit.hasNext()) {
System.out.println(apit.next().getClass().getName());
}
上記のコード スニペットは、アプリケーション サーバーの外部でコードを実行した場合にのみ、魔法のように機能します。Jetty を使用して netbeans で GWT をデバッグしており、アプリケーションは Tomcat にデプロイされています (どちらも同じ動作を示します)。アプリケーション サーバーでコードを実行するとすぐに、コードは ServiceLoader.java のこの時点で失敗します。
public S next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
String cn = nextName;
nextName = null;
Class<?> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
---> if (!service.isAssignableFrom(c)) { <-----
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated: " + x,
x);
}
throw new Error(); // This cannot happen
}
私の意見では、プラグインとインターフェースの両方が適切にロードされていることをデバッグが示しています。それ以外の場合、Class.forName は失敗します。繰り返しますが、これはアプリケーション サーバーがなければ実現しません。サービス情報は META-INF/services/xx.xx.pluginapi.NotificationPlugin にあります。
私の直感によると、エラーはアプリケーション サーバーが ClassLoaders を使用する方法と関係がありますが、私と Google は参照を見つけることができませんでした。この問題を克服する方法を知っている人はいますか?助けていただければ幸いです。