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のような
フレームワークも作れるはずです。僕もいつかフレームワークを作ってみたい
と思っているのですが、アイデアがまだ浮かんでないんですよねー。どうせ作るなら
他に類を見ない斬新なものにしたいですからねぇ。
んじゃ
やまろう