2003/12
「XMLドリブンのフィルターフレームワーク」とはなんぞや?ということですが、
ServletコンテナのServletフィルタや、StrutsのActionクラスみたく、
XMLファイルを読み込んで、その情報を元にクラスを呼び出して、
引数で渡されたオブジェクトを加工して次のフィルタ(Action)に渡していく
っていうもののことです。そういう観点で見るとServletフィルタやActionクラスは
ServletRequest または HttpServletRequest、
ServletResponse または HttpServletResponse
にフィルタをかけるクラスだということが出来ます。
そこで、今回は、Mapオブジェクトに対してフィルタをかけるフレームワークを
作ってみたいと思います。なぜHttpServletRequestではなくMapにしたかと
いうと、コマンドラインで実行したいからです。まずはコマンドライン版で
作って、必要なら引数をHttpServletRequestに修正すればいいってことで。
というか、最近のフレームワークはServletコンテナ上じゃなくても
動くコンポーネントを作らせようという流れがあるのでむしろ、Mapのが
良いのかもしれません。
それじゃソース行ってみよう!(そのままコンパイル出来るように
すべて同一パッケージ、パッケージスコープのクラスにしてあります。)
package filter; import java.util.*; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.parsers.*; class FilterExecuter { /** コンフィグ情報 */ private static Map configMap = getConfig(); /** * 引数nameをキーにコンフィグファイルの内容を取り出し、フィルターを呼び出す。 * @param name コンフィグファイルのfiltersタグのname属性 * @param map フィルタリングするMapオブジェクト */ public static void execute(String name, Map map) { try { String[] classNames = (String[]) configMap.get(name); for (int i = 0; i < classNames.length; i++) { MapFilter filter = (MapFilter)Class.forName(classNames[i]).newInstance(); filter.doFilter(map); } } catch (Exception ex) { throw new RuntimeException(ex); } } /** * コンフィグファイルを読み込み解析してMapオブジェクトに格納し返す。 * @return コンフィグ情報 */ private static Map getConfig() { Map map = new HashMap(); try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); Document doc = builder.parse("filter-config.xml"); Element docElem = doc.getDocumentElement(); NodeList filtersList = docElem.getElementsByTagName("filters"); for (int i = 0; i < filtersList.getLength(); i++) { Element filters = (Element) filtersList.item(i); String name = filters.getAttribute("name"); NodeList filterList = filters.getElementsByTagName("filter"); ArrayList list = new ArrayList(); for (int j = 0; j < filterList.getLength(); j++) { Element filter = (Element) filterList.item(j); String type = filter.getAttribute("type"); list.add(type); } String[] types = new String[list.size()]; list.toArray(types); map.put(name, types); } } catch (Exception ex) { throw new RuntimeException(ex); } return map; } } interface MapFilter { /** * 引数mapをフィルタリングする。 * @param map フィルタリングするMapオブジェクト */ public void doFilter(Map map); } class SampleFilter implements MapFilter { /** * 引数mapにハッシュキー"theData"で登録されてる * オブジェクトを"女"ではさむ。ウハウハなフィルタ。 * @param map フィルタリングするMapオブジェクト */ public void doFilter(Map map) { String data = (String)map.get("theData"); data = "女" + data + "女"; map.put("theData",data); } } /** * 動作確認用クラス */ class Main { public static void main(String[] args) { HashMap m = new HashMap(); m.put("theData", "やまろう"); FilterExecuter.execute("filters1", m); //(1) System.out.println(m.get("theData")); HashMap m2 = new HashMap(); m2.put("theData", "キムタク"); FilterExecuter.execute("filters2", m2); //(2) System.out.println(m2.get("theData")); } }
実行してみましょう!
1.コンフィグファイルを用意(filter-config.xmlという名前で保存)
[filter-config.xml]
<filter-config>
<filters name="filters1">
<filter type="filter.SampleFilter" />
<filter type="filter.SampleFilter" />
<filter type="filter.SampleFilter" />
</filters>
<filters name="filters2">
<filter type="filter.SampleFilter" />
<filter type="filter.SampleFilter" />
</filters>
</filter-config>
2.コンパイル & 実行
このメルマガを適当なファイル名(FilterFramework.java)で
保存して、
>javac -d . *.java
>java filter.Main
女女女やまろう女女女
女女キムタク女女
実行結果の1行目は
(1)でfilters1を指定してるのでSampleFilterが3回実行されて
やまろうが女の子6人に囲まれてます。実際そんなことないけど(シュン)。
実行結果の2行目は
(2)でfilters2を指定してるのでSampleFilterが2回実行されて
キムタクが女の子4人に囲まれてます。キムタク人気ねぇー!?
あほプログラムですみません。でも、Servletフィルタもこれと同じよう
なしくみで動いているんだと思います。内面を見てってことよ!
いかがだったでしょうか?今回のプログラムを応用すれば、Strutsのような
フレームワークも作れるはずです。僕もいつかフレームワークを作ってみたい
と思っているのですが、アイデアがまだ浮かんでないんですよねー。どうせ作るなら
他に類を見ない斬新なものにしたいですからねぇ。
んじゃ
やまろう