Tomcatで動かすWebアプリをプラグインで機能拡張可能にするためにやったこと
ここら辺を参考に。
- プラグインで拡張できるJavaプログラムを作る (1/3):CodeZine(コードジン)
- http://osima.jp/blog/howto-make-plugin-system-improved.html
プラグインのインターフェースを決める
とりあえず適当に決める。
priority付けてるのはプラグインが複数あったときに実行順を決めたかっただけ。
package com.example; public interface FooPlugin { public int getPriority(); public Bar execute(Bar bar); }
プラグイン読み込み管理クラスをつくる
最初URLClassLoaderで試してたんだけど、うまくいかないので、RMIClassLoaderに変更。
package com.example; import java.io.File; import java.io.IOException; import java.rmi.RMISecurityManager; import java.rmi.server.RMIClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.jar.Attributes; import java.util.jar.JarFile; import java.util.jar.Manifest; import java.util.logging.Logger; public class FooPluginManager { private static final Logger logger = Logger.getLogger(FooPluginManager.class.getName()); private ArrayList<FooPlugin> plugins = new ArrayList<FooPlugin>(); public FooPluginManager(String pluginPath) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException { logger.info(pluginPath); if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } File dir = new File(pluginPath); String[] files = dir.list(); for (int i = 0; i < files.length; i++) { if (files[i].toLowerCase().endsWith(".jar")) { File jarFile = new File(pluginPath + File.separator + files[i]); JarFile jar = new JarFile(jarFile); Manifest manifest = jar.getManifest(); Attributes attribs = manifest.getMainAttributes(); String className = attribs.getValue("Plugin-Class"); if (className != null) { className = className.trim(); Class<?> clazz = RMIClassLoader.loadClass(jarFile.toURL(), className); logger.info("load... " + className); Object obj = clazz.newInstance(); plugins.add((FooPlugin) obj); } } } } public ArrayList<FooPlugin> sort() { Collections.sort(plugins, new Comparator<FooPlugin>() { public int compare(FooPlugin a, FooPlugin b) { if (a.getPriority() < b.getPriority()) return -1; if (a.getPriority() > b.getPriority()) return 1; return a.getClass().getCanonicalName().compareTo(b.getClass().getCanonicalName()); } }); for (Iterator<FooPlugin> iter = plugins.iterator(); iter.hasNext();) { logger.info("sort... " + iter.next().getClass().getCanonicalName()); } return plugins; } }
プラグインを書く
2個くらい書いてみる。
package com.example; import com.example.Bar; import com.example.FooPlugin; public class FooPluginImpl1 implements FooPlugin { private static final int priority = 2; public int getPriority() { return priority; } public Bar execute(Bar bar) { return bar; } }
package com.example; import com.example.Bar; import com.example.FooPlugin; public class FooPluginImpl2 implements FooPlugin { private static final int priority = 1; public int getPriority() { return priority; } public Bar execute(Bar bar) { return bar; } }
マニフェストを書いてjarファイルをつくる
EclipseのExport機能を使ってjarファイルをつくる。(任意のマニフェストを指定できる。)
p1.mf
Manifest-Version: 1.0 Plugin-Class: com.example.FooPluginImpl1
メインプログラム
とりあえず読み込んでソートだけしてみる。
FooPluginManager pm = new FooPluginManager("C:\\jakarta-tomcat-6.0\\webapps\\foo\\WEB-INF\\plugins"); ArrayList<FooPlugin> plugins = pm.sort();
jakarta-tomcat-6.0/conf/catalina.policyの変更
とりあえずゆるゆるで。
// ========== FOO CODE PERMISSIONS ======================================= // These permissions apply to foo grant codeBase "file:${catalina.home}/webapps/foo/WEB-INF/classes/-" { permission java.security.AllPermission; }; // These permissions apply to plugin grant codeBase "file:${catalina.home}/webapps/foo/WEB-INF/plugins/-" { permission java.security.AllPermission; };
Tomcat起動引数を追加
デフォルトだとcatalina.policyは使われず、JREのlib/security/java.policyが使われる。-securityオプションを付けるとcatalina.policyを使うようになるとTomcatのドキュメントにあったんだけど、実際に試すとそんなコマンドはないとか言われて困り果てたところ、RE: Simple Security Manager how to ? というのを見つけた。
Configure -> [Java] タブ -> Java Options
-Djava.security.manager -Djava.security.policy=C:\jakarta-tomcat-6.0\conf\catalina.policy
おわり
ログで動作確認。
2011/01/19 10:54:17 com.example.FooPluginManager <init> INFO: C:\jakarta-tomcat-6.0\webapps\foo\WEB-INF\plugins 2011/01/19 10:54:17 com.example.FooPluginManager <init> INFO: load... com.example.FooPluginImpl1 2011/01/19 10:54:17 com.example.FooPluginManager <init> INFO: load... com.example.FooPluginImpl2 2011/01/19 10:54:17 com.example.FooPluginManager sort INFO: sort... com.example.FooPluginImpl2 2011/01/19 10:54:17 com.example.FooPluginManager sort INFO: sort... com.example.FooPluginImpl1