|
||||||||||
| 前のパッケージ 次のパッケージ | フレームあり フレームなし | |||||||||
参照:
説明
| クラスの概要 | |
| CallMethodRule | 後続の CallParamRule または、当該要素のボディから収集した引数を渡して、
最上位(親)オブジェクトのメソッドを呼び出すルールを実装しています。 |
| CallParamRule | CallMethodRule ルールに関連して呼び出され利用される、 要素の属性またはそのボディのどちらか一方からパラメータを取り出し保存するルールを実装しています。 |
| Digester | Digester は、 一連のネストした要素のパターンを検出することによって実行されるルールを、 パースの開始前に追加し、 それによって XML 入力ストリームの処理を行います。 |
| ObjectCreateRule | 新しいオブジェクトを生成しオブジェクトスタックへプッシュするルールを実装しています。 |
| Rule | このクラスの具象的な実装を行うと、XML 要素の中から相応のネストしたパターンが検出された際の動作を組み込むことができます。 |
| SetNextRule | 最上位(子)オブジェクトを引数として渡し、 (top-1)(親)オブジェクトのメソッドを呼び出すルールを実装しています。 |
| SetPropertiesRule | 対応する名前を持つ属性を元に、 スタックの最上位オブジェクトが持つ複数のプロパティの設定を行うルールを実装しています。 |
| SetPropertyRule | 対応する名前の属性を元に、 スタックの最上位オブジェクトが持つ個々のプロパティの設定を行うルールを実装しています。 |
| SetTopRule | (top-1)(子)オブジェクトを引数として渡し、 最上位(親)オブジェクトのメソッドを呼び出すルールを実装しています。 |
Digester パッケージは、独自の XML 文書をルールに基づいて処理する機能を提供します。
XML 形式のデータに対応したアプリケーション環境では "イベント駆動" 方式によって XML 文書の処理が行えると便利です。 "イベント駆動" 方式では、ネストした XML 要素の中から特定のパターンを検出したときに、特定の Java オブジェクトが生成されます(または既存のオブジェクトのメソッドが呼び出されます)。 Simple API for XML Parsing (SAX) による XML 文書のパース[訳注:解析]に精通したデベロッパは、 Digester がより高いレベルの、もっとデベロッパにとって使いやすい SAX イベントのインターフェースを提供していることに気が付くでしょう。 そのため、XML 階層構造の細部をほとんど意識する必要がありません -- デベロッパは、パースした結果の処理に専念することができます。
Digester を使用する手順として、次の基本的なステップが必要になります:
org.apache.struts.digester.Digester クラスのインスタンスを作成します。
以前に作成した Digester インスタンスを安全に再使用するためには、
以前に要求されたパースが完了するのを待ち、また複数のスレッドからその Digester
インスタンスを利用しないことです。digester.parse() メソッドを呼び出します。
パースされる XML 文書への参照は、複数の形式のうちいずれかで渡します。
詳細に関しては Digester.parse()
のドキュメントを参照してください。パーサからスローされる
IOException や SAXException 、
あるいはプロセッシングルールのいずれかからのスローされる実行時例外を、
キャッチするよう備えておく必要があることに注意してください。org.apache.struts.digester.Digester
インスタンスには、いくつかのプロパティがあり、それを操作しカスタマイズすることができます。
それらのプロパティはパースに影響を及ぼします。
parse() メソッドのひとつを呼び出す前に必ず設定して下さい。
プロパティ 説明 debug この整数値は、デバッグ時にパースの経過を System.out()に出力し、その出力量を定義します。 これはパース時に発生した問題を追跡する際に役立ちます。 デフォルトの値は 0 で、デバッグ出力を行いません -- 0 以上の値を設定するとより多くの、 そして詳細なデバッグ情報を生成します。validating このブール値は trueを設定することでDOCTYPE宣言で定義されたドキュメントタイプ定義 (DTD) に対する XML 文書の妥当性を検査します。 デフォルトの値はfalseで、 "妥当な" ではなく "整形式" の XML 文書を単に検出します。
上のプロパティの定義に加えて、ローカルにコピーしたドキュメントタイプ定義 (DTD) を
DOCTYPE 宣言の参照に、追加して記述することができます。
システム外を参照するパブリックな識別子を伴う
DOCTYPE 宣言を検出したときに、
XML パーサはそのような記述を識別して、実際には DOCTYPE 宣言のものではなく、
常にその記述したシステム識別子 (URL) の指す
DTD の内容を利用するでしょう。
例えば、 Struts フレームワークを動かしているサーブレットは、 Struts 設定ファイルを参照する際に、ローカルにコピーされた DTD を使うよう、 Struts に指示します。 これは Struts がインターネットへ接続せずに環境設定を行えるようにし、 (それがネットワークへの接続を回避するため) インターネットサイトへの接続を伴う処理と比べてスピードアップを図れます。
digester.register
("-//Apache Software Foundation//DTD Struts Configuration 1.0//EN",
"/org/apache/struts/resources/struts-config_1_0.dtd");
ちなみに、この例のなかで使用されているシステム識別子は、
java.lang.ClassLoader.getResource() または
java.lang.ClassLoader.getResourceAsStream()
へ渡されるパスです。実際の DTD リソースは、全ての
Struts クラスを(大抵は struts.jar ファイルから)
ロードする、それらと同一のクラスローダによってロードされます。
org.apache.struts.digester.Digester テクノロジの最も一般的な使用法は、
動的に Java オブジェクトのツリーを構築するというものです。ツリーの内部構造は、
(ツリー内のオブジェクトの細かいプロパティ設定を行ったかのように)
XML ドキュメントの内容に基づいた設定がなされます。
実際、 Digester パッケージを作成した主な理由は、
Struts のコントローラサーブレットが自身の設定を簡単に、
アプリケーション配下にある struts-config.xml
ファイルの内容に基づいて行えるようにすることでした。
使い方を簡単にするために、 Digester はスタックを開示していて、それを、 要素の照合パターンを満たした場合に発生するプロセッシングルールから操作できるようにしています。 利用できる一般的なスタック関連の操作を、次に示します:
典型的な設計手法では、特定の XML 要素を見つけたときに、特定のルールを発生させ、 そこで新しいオブジェクトを生成しスタックへプッシュします。 そのネストが処理されている間オブジェクトはそこに置かれ、そして要素の最後に到達したときにポップされます。 この標準 "オブジェクト生成" プロセッシングルールは、 とても便利な方法として、この機能の実現に一役買っています。
Digester のこの機能とは別の特性が投げかける、設計手法に関して危惧されるいくつかの問題点:
org.apache.struts.digester.Digester
パーサの主な特徴は、 Digester が XML
ドキュメント要素の階層構造を自動的に検索してくれるため、
デベロッパがプロセスを意識する必要がないことです。
その代わり、 XML ドキュメントのパースに際して、
検出したい要素のネスト構造と、
そのとき、どのような機能を実行すればよいのかを決めることに専念します。
このような仕組みのことを要素のパターン照合と呼びます。
最もシンプルな要素の照合パターンは "a" のような単純な文字列でしょう。
このパターンは、XMLドキュメントの中で <a> が最上位要素として現れるすべての箇所と(何回でも)一致します。
ですが、ネストの中に <a> という要素がある場合には、このパターンと一致しません -- この種のパターン照合に関してこの後、述べていきます。
次に "a/b" の組み合わせにステップアップします。
このパターンは <a> を最上位の要素としたネストの中に <b>
が見られる場合に一致します。
先程も言った通り、 XML ドキュメントの内容に従った回数だけ一致します。
複数のスラッシュを使って検索したい階層構造の深さをはっきりさせることで、
適切な照合を行うことができます。
例として、 "a"、 "a/b"、 そして "a/b/c" というパターンを検出するプロセッシングルールが登録してあるものと仮定します。 次の内容のような XML ドキュメントが入力された場合、パースによって、 それぞれのパターンは対応する要素と一致します:
<a> -- "a" と一致
<b> -- "a/b" と一致
<c/> -- "a/b/c" と一致
<c/> -- "a/b/c" と一致
</b>
<b> -- "a/b" と一致
<c/> -- "a/b/c" と一致
<c/> -- "a/b/c" と一致
<c/> -- "a/b/c" と一致
</b>
</a>
さらに、パターン文字列の中で "*" ワイルドカード文字を使用することによって、
特定の XML 要素がどれだけネストしていても(またはネストしていなくても)
検索することができます。
例えば、<a> がドキュメント中のネストのどの位置にあっても、
"*/a" という要素の照合パターンで検出することができます。
そのため、ひとつ要素がパースされたとき、 複数の登録されたプロセッシングルールに対応するパターンを検出する可能性も十分にあります (複数のプロセッシングルールを同じ照合パターンに登録したか、 厳密な照合パターンとワイルドカードによる照合パターンの両方が同じ要素によって満たされたため)。 その場合、Digester に登録した順番の最初から、対応する全てのプロセッシングルールが発生します。
前節の記述では、 どうすれば思い通りのタイミングで処理を行えるか、その方法を説明しました。 プロセッシングルールの目的は、あるパターンを検出したときに何をすればいいのかを定義することです。
形式的なことを言えば、プロセッシングルールとは org.apache.struts.digester.Rule を実装した Java クラスのことです。 各々のルールには下記のイベントメソッドがひとつ以上組み込まれ、 検出したパターンに対応するルールが発生したときに定義されている限り何度でも呼び出されます:
Digester を設定した後、addRule() メソッドを呼び出すことで、
要素の照合パターンを、上記のタイミングで呼び出される、イベントを捕捉するメソッドを持つ
Rule クラスのインスタンスと一緒に登録することができます。
この仕組みによって、 アプリケーションに様々な機能を組み込むために、
動的に Rule を実装したクラスを生成することができます。
加えて、プロセッシングルールを実装したクラスのセットが提供されており、 多くのプログラミングに共通するシナリオに対処できます。次にそのクラスを記述します:
begin() メソッドが呼び出された場合、このルールは、指定された Java
クラスのインスタンスを新規に生成し、それをスタックへプッシュします。
クラス名は、特に指定がなければ、このルールのコンストラクタに渡されたパラメータのものを使用し、
オプションとして、処理対象の XML ドキュメントに定義された属性を経由して classname を
渡し上書きすることができます。
end() メソッドが呼び出された際、
スタック上の一番上にあるオブジェクト(おそらく、begin()
メソッドの中で追加されたもの)はポップされ、( Digester
の中にある)それへの参照も全て消去されます。begin() メソッドが呼び出された際、
Digester は、指定された XML 要素の中のプロパティ名から
(Digester 内にあるスタックの最上位オブジェクトが持っている) JavaBeans
のプロパティを設定するメソッドを、 標準の Java リフレクション API を使って取得し、
対応する属性値を渡してそれぞれ呼び出します。
一般的な動作の順序として、ひとつの要素の照合パターンについて、オブジェクト生成のルール、
次いでプロパティを設定するルールが記述されます。
その理由は、 新規の Java オブジェクトが生成され、
次いで、その生成されたオブジェクトと同じ XML 要素の属性を元に、
その生成されたオブジェクトの一連のプロパティを "設定" することによります。begin() メソッドが呼び出された際、
Digester は、Digester 内にあるスタックの最上位オブジェクトから、
指定された(属性によって指定されるプロパティ名の)
プロパティを設定するメソッドを、(それとは別の属性によって指定される)値を指定して、
呼び出します。
これは XML ファイルが固有の DTD を参照する場合に、 DTD
の中に対応する属性が無いプロパティを個別に設定したい場合に便利です。begin() メソッドが呼び出された際、
Digester はスタックの最上位の次の要素を分析し、
指定されたプロパティを設定するメソッドを探します。
その後、スタックの最上位オブジェクトを引数として、このメソッドを呼び出します。
このルールは2オブジェクト間に一対多の関係を確立する場合に一般的に使用され、
メソッド名には一般に "addChild" といった名前を付けます。begin() メソッドが呼び出された際、
Digester はスタックの最上位の要素を分析し、
指定されたプロパティを設定するメソッドを探します。
その後、スタックの最上位の次のオブジェクトを引数として、このメソッドを呼び出します。
このルールは SetNextRule とは別のもうひとつの手段として、オブジェクトクラスの API
に採用することができ、メソッド名には一般に "setParent" といった名前を付けます。end() メソッドが呼び出された際、
Digester 内にあるスタックの最上位オブジェクトの指定されたメソッドを呼び出します。
呼び出されるメソッドの名前、引数の数、そしてオプションとして、メソッドの(複数の)引数の型に
(複数の)Java クラス名を指定して、このルールの設定を行います。
もし、このルールを発生させた要素にネストしたボディ部分があれば、
次に記述する CallParamRule を使用して、その累積を実際のパラメータの値とします。上述したように、 digester.addRule() を呼び出すことによって、
標準の Rule クラスのインスタンスを生成し、登録することができます。
しかしながら、そのような方法は汎用的なものなので、短縮して記述できるよう、各標準ルールを
Digester クラスに直接、登録するためのメソッドが定義されています。
次にコード例を示します:
Rule rule = new SetNextRule(digester, "addChild",
"com.mycompany.mypackage.MyChildClass");
digester.addRule("a/b/c", rule);
このように置き換えられます:
digester.addSetNext("a/b/c", "addChild",
"com.mycompany.mypackage.MyChildClass");
前述したように、 org.apache.struts.digester.Digester
パッケージは、Struts コントローラサーブレット自身が Struts
ベースアプリケーションのあらゆる局面についての設定を struts-config.xml
に記述し、その内容を処理する、頑丈で、柔軟な、簡単に拡張できるメカニズムを実現するために在ります。
このことで、コントローラサーブレットが包括的な、また実装面での
Digester を使用する事例になるといえます。
Digester を使用するために、その生成と設定をする方法については
org.apache.struts.action.ActionServlet クラスの initDigester()
メソッドを、
また実際にどこでパースを行っているのかについては initMapping()
メソッドを参照してください。
ここからは、Digester のいくつかの特徴を活かした使用法を解説するために、 いくつかの照合パターンとプロセッシングルールの構成に焦点を当てて述べます。 まず、 どのようにして Digester インスタンスが生成され、初期化されるのかを見てみましょう:
Digester digester = new Digester();
digester.push(this);
digester.setDebug(detail);
digester.setValidating(true);
ここでは Digester インスタンスが生成され、パーサの妥当性検査を有効にしているのがわかります。 (前述したように)妥当性検査は、Struts に含まれる struts-config_1_0.dtd DTD に対して行われます。 設定されたオブジェクトを追跡できるようにする意味で、コントローラサーブレットのインスタンスは、 自身を Digester 内にあるスタックに追加しています。
digester.addObjectCreate("struts-config/global-forwards/forward",
forwardClass, "className");
digester.addSetProperties("struts-config/global-forwards/forward");
digester.addSetNext("struts-config/global-forwards/forward",
"addForward",
"org.apache.struts.action.ActionForward");
digester.addSetProperty
("struts-config/global-forwards/forward/set-property",
"property", "value");
これらの記述で、グローバルフォワード宣言を処理するためルールが生成されます。
こうすることで <forward> 要素を見つけた場合に、次のような動作をします:
ActionForward
インスタンスと定義しています。(forwardClass String 型変数に格納される)
Java クラス名が初期化パラメータで指定されなかった場合でも、 (もし現在、パース中の XML
の要素に含まれている場合) "className" 属性を使用することによって上書きできます。
新しい ActionForward インスタンスはスタックにプッシュされます。ActionForward
インスタンスのプロパティは <forward>
要素の属性によって設定されます。<set-property> がネストされている場合は、
追加プロパティを設定するメソッドが呼び出されます。
これら、 DTD には含まれない ActionForward
クラスの追加プロパティは、自分で組み込む必要があります。ActionForward
のインスタンス)を引数として、スタックの最上位の次のオブジェクト
(つまりコントローラサーブレット自身)の addForward()
メソッドが呼び出されます。
これによってグローバルフォワードが登録され、
また、最終的にスタックからポップされた後も保持されることになります。<forward> 要素の最後でスタックから最上位のオブジェクト
(つまり ActionForward のインスタンス)がポップされます。その後、Digester は以下のように実行されます:
InputStream input =
getServletContext().getResourceAsStream(config);
...
try {
digester.parse(input);
input.close();
} catch (SAXException e) {
... deal with the problem ...
}
parse() の呼び出しの結果、 struts-config.xml
ファイルで定義された全ての設定情報は、Struts
のコントローラサーブレット内にキャッシュされたオブジェクトの集合として表され、
サーブレットコンテキスト属性として利用することができます。
Digester モジュールは XML ファイルの単純な要素や属性ばかりでなく、
ネストされたボディテキストも処理することがでます。
以下の例は、カレント web アプリケーションのための、 web アプリケーションのための配備記述子(
/WEB-INF/web.xml )をパースし、
特定のサーブレットのために設定情報を記録する必要がある、
という仮定を基づいたものです。
この情報を記録する上で、以下のようなメソッド(など)を持つ
Bean クラスがあるものと仮定します:
package com.mycompany;
public class ServletBean {
public void setServletName(String servletName);
public void setServletClass(String servletClass);
public void addInitParam(String name, String value);
}
典型的な Struts ベースアプリケーションのコントローラサーブレットを宣言する
web.xml ファイルの処理に関して見ていきます
(この例では簡潔にするために要約しています):
<web-app>
...
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet<servlet-class>
<init-param>
<param-name>application</param-name>
<param-value>org.apache.struts.example.ApplicationResources<param-value>
</init-param>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml<param-value>
</init-param>
</servlet>
...
</web-app>
次に、この入力ファイルのための Digester プロセッシングルールを定義しましょう:
digester.addObjectCreate("web-app/servlet",
"com.mycompany.ServletBean");
digester.addCallMethod("web-app/servlet/servlet-name", "setServletName", 0);
digester.addCallMethod("web-app/servlet/servlet-class",
"setServletClass", 0);
digester.addCallMethod("web-app/servlet/init-param",
"addInitParam", 2);
digester.addCallParam("web-app/servlet/init-param/param-name", 0);
digester.addCallParam("web-app/servlet/init-param/param-value", 1);
すると、次の処理が行われ、要素がパースされます:
com.mycompany.ServletBean
オブジェクトが生成され、スタックにプッシュされます。ServletBean )の
setServletName() メソッドが呼び出されます。ServletBean )の
setServletClass() メソッドが呼び出されます。ServletBean
)の addInitParam メソッドを呼び出す準備がなされますが、
この時点では呼び出されません。
続いて呼び出されるパラメータルールによって、2つの String
パラメータが準備されるまで呼び出しを待機します。addInitParam()
が呼び出され、新しい名称の組み合わせが Bean に格納されます。addInitParam()
の呼び出しが行われます。ServletBean
)がオブジェクトスタックからポップされます。
|
||||||||||
| 前のパッケージ 次のパッケージ | フレームあり フレームなし | |||||||||