プラグイン
Nexaweb Client でプラグインがよく使用されるケースとして、高度なグラフ作成機能が必要な場合などがあります。Nexaweb Client にはもともとグラフ機能が備わっていますが、財務アプリケーションで見られるような、より高度なグラフを表示するには不十分な場合もあります。開発者は、 プラグインアーキテクチャを使用して、選択したサードパーティ製のグラフコンポーネントを統合し、そのグラフを表示するための独自の XML タグやフォーマットを定義できます。その後、このグラフを標準の NXML 内に埋め込むことができます。
例:
<window> <borderLayout/> <label text="A custom chart" borderPosition="north" /> <!-- a specialized chart created via the plug-in architecture --> <financialChart title="Dow Jones by year" borderPosition="center" onSelect="myPage.jsp> <xAxis start="1950" end="2005"/> <yAxis start="0" y="5000"/> <pointData points="1900,2000,2020 ... "/> </financialChart> </window>
最 も基本的なレベルでは、プラグインはcom.nexaweb.client.tags.AbstractTagImpl を拡張するクラスで、XML 要素への変更を待機します。このような変更は、com.nexaweb.xml.events.StructureChangeEvent と com.nexaweb.xml.events.AttributeChangeEvent の形で発生します。これらのクラスは、それぞれのタグ名に独自の処理クラスを持つことから、タグ ハンドラと呼ばれます。また、これらのクラスは、ピアという実装クラスとXML 間にブリッジを形成するため、ブリッジクラスとも呼ばれます。
AbstractTagImpl はすべてのプラグインの基底クラスです。また、特定タイプのプラグインの作成を容易にするために、多くのサブクラスがあります。たとえば、 ContainerBridge または SwingBridge をサブクラスとして使用し、java.awt.Component または javax.swing.JComponent に基づいて UI コンポーネントを作成できます。また、JComponent を使用して、カスタムの java.awt.LayoutManager クラスを実装することも可能です。
上記の例では、開発者は、ピアとして機能するサードパーティ製のグラフ作成コンポーネントを使用します。AbstractTagImpl を拡張する処理クラスには、ピアクラスへの変更に応答すると共に、XML への変更に基づいてこれらのピアを操作する役割があります。
AbstractTagImpl と そのサブクラスは、StructureChangeEvents と AttributeChangeEvents、およびそれらに関連付けられている XML 要素を自動的に待機します。各サブクラスは、属性が変更されたり、子タグが追加および削除されたときに、意味のある操作を行います。同様に、サブクラスは ピアオブジェクトへの変更を待機して、イベントを発生させるか、このような変更に基づいて UI DOM (Document Object Model) を更新できます。ブリッジクラスは双方向のブリッジで、XML への変更を待機してその変更をピアに渡します。また、ピアへの変更を待機して、イベントを発生させるか、その変更に基づいて DOM を更新します。
プラグインが適切な JVM で実行されるよう注意する必要があります。Swing に基づく UI コンポーネントのプラグインは、Swing 対応の JVM (1.2 以上) でのみ実行されます。
JButton プラグインの作成
こ こでは、<jButton> タグを JButton コンポーネントにマップする、シンプルなプラグインを作成します。<jButton> タグは bgColor などの基本属性を認識します。また、<jButton> タグは、"armed" (作動準備完了) という属性をブール値として実装し、アクティブになったときに、onCommand イベントを発生させます。
<jButton> タグを作成する最初の手順では、<jButton> タグにマップするタグ ハンドラを作成します。JButton は Swing ベースのコンポーネントであるため、使用するクラスによって SwingBridge が拡張されます。<jButton> タグが最初に検出されると、パーサーは JButtonBridge のインスタンスをインスタンス化して、それに対する初期化メソッドを呼び出します。このようなメソッドの 1 つであるinit() は、順に createUiComponent() を呼び出します。次に示すように、このメソッドをオーバーライドして必要な UI コンポーネントを作成する必要があります。
public class JButtonBridge extends SwingBridge { public void createUiComponent(){ setUiComponent(new JButton()); } }
こ の時点で、ブリッジクラスは JButton コンポーネントを作成して、bgColor などの基本属性に自動的に応答します。また、このタグを "armed" 属性に応答させる必要があります。そのためには、新しい属性を処理するために onAttributeSet() をオーバーライドします。
public void attributeSet(AttributeChangeEvent e) throws AttributeConversionException { String attributeName = e.getName(); String attributeValue = e.getNewValue(); //if the attribute is "armed" set it to true or false if ("armed".equals(attributeName) ){ ( (JButton)getUiComponent() ).getModel(). setArmed(AttributeConverter.toBoolean(attributeValue)); } //otherwise just fall through else super.attributeSet(e); }
AttributeConverter は、文字列をブール型、Color、列挙型、その他のデータ型に変換するメソッドを提供するユーティリティクラスです。attributeSet をオーバーライドしてAttributeConverter を使用すると、XML でのすべてのエラーが通常の方法で報告されます。
<jButton> が属性の変更に応答できるようになったので、今度はJButton ActionListener が呼び出されたときに、<jButton> が "onCommand" イベントを報告する必要があります。そのためには、ActionListener を実装して、それ自体をリスナとして JButton に追加します。その後で、actionPerformed() が呼び出されたら、イベントの発生を処理する AbstractTagImpl からメソッドを呼び出すだけです。次に、JButtonBridge の最終的なコードを示します。最近行われた変更内容がハイライトされています。
public class JButtonBridge extends SwingBridge implements ActionListener{ public void createUiComponent(){ JButton b = new JButton(); setUiComponent(b); //listen for action events so we can report them b.addActionListener(this); } public void actionPerformed(ActionEvent e){ //call the method from AbstractTagImpl that reports the event //fill in a ClientEvent object and calls the appropriate //MCO/JSP/event handler fireEvent("onCommand",null); } public void attributeSet(AttributeChangeEvent e) throws AttributeConversionException{ String attributeName = e.getName(); String attributeValue = e.getNewValue(); //if the attribute is "armed" set it to true or false if ("armed".equals(attributeName) ){ ( (JButton)getUiComponent() ).getModel(). setArmed(AttributeConverter.toBoolean(attributeValue) ); } //otherwise just fall through else super.attributeSet(e); } }
JButtonBridge が完了したので、今度は<jButton> タグがJButtonBridge クラスによって定義および処理されるようにマッピングを追加する必要があります。そのためには、適切なエントリを使用してタグマッピングファイルを作成す る必要があります。次に、クライアント構成内のタグマッピングファイルのリストにそのファイルを追加します。タグマッピングファイル自体は、テキスト形式 の処理クラス名を持つタグ名のリストに過ぎません。このファイルを customTagMappings.xml と呼びます。
<tag-mappings> <jButton>JButtonBridge</jButton> </tag-mappings>
このファイルが作成されたら、エントリを nexaweb-client.xml に追加する必要があります。
<client-tag-mapping> <tag-map-file>customTagMappings.xml</tag-map-file> </client-tag-mapping>
プラグインアーキテクチャクラスの概要
AbstractTagImpl - すべてのプラグインの基底クラスです。プラグインクラスのライフサイクル
新しい NXML ブロックが既存の UI DOM に追加されると、次の処理が実行されます。
- ルート要素に関連付けられているクラスの名前が検索されます。
- ロードされていないクラスがロードされます。
- ハンドラ クラスの新しいインスタンスが作成されます。
- setSession() が現在の ClientSession を使用して、ハンドラで呼び出されます。
- ハンドラが StructureChangeListener と AttributeChangeListener として NXML 要素に追加されます。
- setElement() が NXML 要素を使用して、ハンドラで呼び出されます。
- init() がハンドラで呼び出されます。
- "onCreate" イベントがハンドラで発生します。
NXML block はUI DOMから削除されます
:- unload() はroot 要素に対しハンドラに呼び出されます。
- ハンドラは、StructureChangeListener と AttributeChangeListener として NXML要素から削除されます。
- ハンドラが、再帰的にroot Element のそれぞれの子要素が削除されるプロセスを繰り返す afterChildRemoved() を呼び出します。
ハンドラクラスが onChildRemoved() より優先になる場合、 afterChildRemoved() を呼び出し、再帰的クリーンアップが確実に行われるようにします。
プラグインのパッケージ化
プラグインは次のリソース3セットから構成されています。タグマッピングファイル, タグハンドラクラス、ピア クラス。この例では JButton ピアクラスは全ての Swing-compatible JVM に含まれていますが、ピアクラスが別の JAR ファイルに含まれる場合もあります。
タグハンドラクラスとピアクラスは MCO と同様の開発ルールに従っており、tag-タグマッピングファイルは標準ファイルとして考慮されます。タグマッピングファイルとクラスを一緒に JAR ファイルに入れるのが簡単だと感じる開発者もいるでしょう。例えば、JButtonBridge クラスと カスタムTagMappings.xm は "jButtonPlugin.jar." と呼ばれるJAR ファイルに入る可能性があります。その場合 nexweb-client.xml クラスパスを変更し、その jar を含み、tag-mapping location を変更し"classpath://" specifier を使用しなければなりません。詳細は、クライアントクラスパスの調整に関しての詳細は nexaweb-client.xml のドキュメントご確認下さい。
<!-- classpath changed to include our plug-in jar file --> <client-classpath> <pre-loaded-in-applet-def> <archive name="jButtonPlugin.jar" path="/WEB-INF/client/lib/jButtonPlugin.jar"/> </pre-loaded-in-applet-def> </client-classpath> <!-- load the tag mapping file from the classpath --> <client-tag-mapping> <tag-map-file>classpath://customTagMappings.xml</tag-map-file> </client-tag-mapping>
パッケージングの最終的なオプションは、プラグインに対応しているタグが最初にエンカウンタする時より、起動時に プラグインクラスをロードさせることです。起動時にロードされたクラスは Nexaweb がアニメーションをローディングしている間ロードしますが、一方でオンザフライでロードされた大きなプラグインが原因で、Nexaweb アプリケーションが数秒フリーズする可能性があります。アプリケーションが常に大きいプラグインローディングを使用する場合、起動時にローディングするのが良い選択でしょう。これは、次のようにタグマッピングファイルのコンテンツを修正することによって行う事ができます :
<tag-mappings> <jButton loadPolicy="onStartup">JButtonBridge</jButton> </tag-mappings>
プラグイン名前空間の定義
名前空間はタグセットに対し定義されます。例は Nexaweb Data Framework で、プラグインアーキテクチャを使用し実装しています。このプラグインを定義する XML は以下になります :
<tag-mappings namespace="http://nexaweb.com/data" document="bindings"> <mapping class="com.nexaweb.plugin.data.bridge.BindingBridge" name="binding"/> </tag-mappings>
これはnexaweb.com/data namespaceでタグ “binding” を宣言します。
ビルトインコンポーネントをオーバーライド
プラグインはビルトイン NXML タグの振る舞いを置換えるために使用されます。例えばプラグインが <button> として定義される場合、ボタンのビルトインビヘイビアを置き換えます。