Requires Platform 4.2+
Plug-ins
Nexaweb Client プラグインアーキテクチャは、開発者が独自のタグや属性、XML シンタックスを実装できるよう設計されており、これによってNexaweb Java アプリケーション用Nexaweb Client の機能を拡張できます。通常、開発者は新しいUI コンポーネントを通じて機能を拡張しますが、論理演算やレイアウトペインなどのカスタムロジックを実装することも可能です。
Nexaweb Client での一般的な使用例の1 つとして、高度なチャート機能の使用が挙げられます。Nexaweb Client には組み込みのチャート機能が付属していますが、この機能は財務アプリケーションに見られるような高度なチャートの表示には不十分な場合があります。プラ グインアーキテクチャを使用すると、開発者はサードパーティのチャートコンポーネントを統合して独自のXML タグを定義し、フォーマット設定してそのチャートを表示することができます。その後このチャートを標準のXAL の内部に組み込むことができます。
例:
<window> <borderPane/> <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 と互換性のある(1.2+) JVM でしか実行できません。
JButton プラグインの開発
ここでは、JButton コンポーネントに<jButton> タグをマップする簡単なプラグインを開発します。<jButton> タグは、backGroundColor などの基本的な属性を認識します。また、<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 が完了したので、次はJButtonBridge クラスによって<jButton> タグが定義、処理されるよう、マッピングを追加する必要があります。これには、適切なエントリでタグマッピングファイルを作成する必要があります。その 後、このファイルをクライアントコンフィギュレーション内のタグマッピングファイルのリストに追加します。タグマッピングファイル自体は、ハンドラクラス の名前がテキストとして記載された、単なるタグ名のリストです。このファイルを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>
Plug-in Architecture Class Survey
次の表は、plug-in関連クラスをリスト化し概要を説明しています。
Class | 説明 |
AbstractTagImpl | 全てのplug-inのベースクラス |
ContainerBridge | UI component plug-inのベースクラス。このクラスは全てのjava.awt.Componentsに適用する共通属性に対応している。 |
SwingBridge | Swing-based plug-ins のためのContainerBridge のサブクラス |
LayoutBridge | カスタムLayoutManager tagを作成するためのAbstractTagImplのサブクラス |
AttributeConverter | データtypeに属性値を変換し、エラーを統一し報告するユーティリティクラス |
Plug-in Class Lifecycle
新しいXALブロックが既存のUI DOMに加えられるとき :
1. ルートエレメントに関連したクラス名が検索される
2. クラスがロードされていなければ、ロードされる。
3. handler クラスの新しいインスタンスが作成される。
4. 現在の ClientSession で setSession() がハンドラから呼び出される。
5. HandlerはStructureChangeListener とAttributeChangeListener として XAL elementに追加される。
6. setElement() はXAL element で handler に呼び出される。
7. init() はhandler に呼び出される。
8. "onCreate" イベントはハンドラにより呼び出される。
このプロセスは全てのXAL ブロックで一回のみ行われます。そのブロックのroot elementは、それに設定された子および/または最初の属性を持っている可能性があります。AbstractTagImplは、これらの状況に対処す るため、init() に呼び出されることができる2つのメソッドを提供しています。"parseInitialChildren()" メソッドはこのパーシングプロセスを初期elementの全ての子に反復し行います。"parseInitialAttributes()" メソッドは、element作成後に設定されたかのように、onAttributeSet()で初期elementの属性をフィードします。加え、 ContainerBridgeは、その init() メソッドでデフォルトによりこれらのメソッドを呼び出します。
XAL blockがUI DOMから除外された場合 :
- unload() は root element に対し、handler に呼び出される。
- handler は StructureChangeListener とAttributeChangeListener として XAL element から除外される。
- handler は除外された root Element の各子エレメントのプロセスを再帰的に反復する afterChildRemoved() を呼び出します。
onChildRemoved()に優先するhandler classが存在する場合、再帰的更新が発生するようにafterChildRemoved()を呼び出さなくてはなりません。
Packaging Plug-Ins
プラグインはタグマッピングファイル、タグハンドラクラス、ピアクラスの3つのリソースで構成されています。我々の例では、JButton ピアクラスは全ての Swing の互換性のある JVM に含まれますが、時によりピアクラスが別の JAR ファイルに含まれます。
Tag-handling class とピアクラスは MCO と同じ展開ルールに従っていますが、タグマッピングファイルは、標準ファイルとして見なされます。一部の開発者は、タグマッピングファイルとクラスを組み 合わせ、JAR ファイルに入れるのが簡単だということがわかるかもしれません。例えば、JButtonBridge クラスとcustomTagMappings.xml は "jButtonPlugin.jar." と呼ばれるものの中に JAR ファイル化されています。その場合、nexweb-client.xml classpath を変更しその jar を含み、タグマッピングロケーションを変更し"classpath://" specifier. を使用します。nexaweb-client.xml のドキュメントの client classpath 調整についての詳細をご覧ください。
<!-- 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>
最後一つのパッケージオプションは、スタートアップで、そのプラグインにあったタグが初めて遭遇した時に、plug-in class をロードさせることです。スタートアップでロードされたクラスは Nexaweb のローディングアニメーションの最中にロードされますが、一方で、オンザフライでロードされた大規模 plug-in だと Nexaweb のアプリケーションが数秒フリーズしてしまいます。アプリケーションが、大規模 plug-in をコンスタントに、また頻繁に使用される場合、スタートアップの際ロードする方が良いです。これは、以下のように tag-mapping を修正することで可能です。
<tag-mappings> <jButton loadPolicy="onStartup">JButtonBridge</jButton> </tag-mappings>
Plugin Namespaceの定義
Namespaces は、タグ一式で定義することができます。以下の例は、Nexaweb Data Frameworkで、これはプラグイン アーキテクチャを使用し実装されます。このプラグインを定義するXMLは以下です。
<tag-mappings namespace="http://openxal.org/core/data" document="bindings"> <mapping class="com.nexaweb.plugin.data.bridge.BindingBridge" name="binding"/> </tag-mappings>
ここではopenxal.org/core/data namespace のタグ "binding" を宣言しています。
組込みコンポーネントより優先される
プラグインは組込み XAL タグの振る舞いを置換えるために使用することができます。例えば、プラグインが <button> として定義されると、button の本来の振る舞いを置換えます。
Packaging and Distributing Nexaweb Application Plug-ins
Nexaweb アプリケーション用プラグインのパッケージ化と配布
Nexaweb 4.2.x で Nexaweb アプリケーションに使用するプラグインをパッケージ化して配布するには、次の作業を行う必要があります。
- プラグインのマニフェスト ファイルでプラグインに関する情報を指定する
- プラグインの JAR ファイルをアプリケーションの WEB-INF/client/plugins ディレクトリに配置する
プラグインのマニフェストファイルへのプラグイン情報の追加
アプリケーションに作成する各プラグインに関する情報を、プラグインのマニフェストファイル、plugin.xmlに追加する必要があります。Nexaweb では、プラグインのマニフェストを使用して、プラグインのコンテンツを決定します。
各プラグインの plugin.xml ファイルに次のセクションを追加します。
セクション | 説明 |
<plugin> </plugin> | このアプリケーションにインクルードする固有のプラグインを特定します。 開始タグには、ライフサイクルマネージャまたはプラグインの抽象 MCO を指定する、オプションの class 属性をインクルードできます。 |
<info> </info> | プラグイン スキーマとスタイルシートの場所など、プラグインに関する一般的な情報を指定します。 |
<tag-mappings> </tag-mappings> | プラグインがアプリケーションに追加するタグを定義し、複合タグの定義ファイルの場所と、アプリケーションがプラグインをロードする際に使用するポリシーを指定します。 |
次に、プラグインのマニフェストファイルのサンプルを示します。
<plugin class="MyPlugins"> <info> <provider-name>Nexaweb Technologies</provider-name> <author>James</author> <description>This plug-in automates automatic automation.</description> <version>10.3.4</version> <schema-location>schema/plugin.xsd</schema-location> <stylesheet-location>stylesheet/stylesheet.xss</stylesheet-location> </info> <tag-mappings namespace="http://nexaweb.com/charts" document="nxml"> <mapping name="barChart" class="com.nexaweb.plugins.BarChart" icon="barChart.gif" loadPolicy="onStartup"/> <mapping name="loginDialog" class="com.nexaweb.plugins.LoginDialog" icon="loginDialog.gif" definitionFile="ui/loginDialog.nxml"/> </tag-mappings> </plugin>
次の表では、<info> セクションに含まれるタグについて説明します。
info タグ | 説明 |
<provider-name> </provider-name> | プラグインの作成者に関する情報を指定します。 |
<author> </author> | プラグインの作成者に関する情報を指定します。 |
<description> </description> | プラグインの目的または使用に関する情報を提供します。 |
<version> </version> | プラグインのバージョン番号を指定します。 |
<schema-location> </schema-location> | このプラグインのスキーマ ファイルの場所を特定します。 |
<stylesheet-location> </stylesheet-location> | このプラグインのスタイルシートの場所を特定します。デフォルトの配布先は、JAR ファイルが配置されているディレクトリ内の /stylesheet です。 |
次の表では、<tag-mapping> セクションに含まれるタグの属性について説明します。
tag-mapping タグの属性 | 説明 |
namespace | これらのタグマッピングが属する名前空間を指定します。 |
mapping name | アプリケーションに追加されるプラグインで使用されるタグ名を指定します。 |
マッピング名の属性
属性 | 説明 |
class | このタグが属するクラスを指定します。このクラスは、lib または classes フォルダ内に存在する場合があります。 |
icon | このタグに関連付けられている任意のイメージを指定します。イメージは、イメージフォルダに対して相対的な位置にあります。 |
loadpolicy | この属性を使用して、Nexaweb の起動時にこのプラグインを起動することを指定します。この属性の有効な値は onStartup のみです。この属性がタグマッピングに存在しない場合は、このタグが最初に検出されたときにプラグインが起動します。 |
definitionFile | 複合タグに含まれる複数の関数を定義するファイルの場所を指定します。 |
プラグインディレクトリの構造
Nexaweb Studio では、ビルドおよび配布に独自のディレクトリ構造を使用してプラグインが構成されます。
プラグインのビルドには、次の表に示すディレクトリ構造が使用されます。
ビルド用ディレクトリ | 含まれる内容 |
/source | Java ソースコード。 |
/dependencies | プラグインと一緒にパッケージ化されない依存関係で、実行時に存在する必要があります。 |
/dependencies/lib | JAR ファイル (例: jaxp.jar)。 |
/dependencies/plugins | このプラグインが依存する他のプラグイン (例: data-interfaces-plugin.jar)。 |
/PluginContent | Web アプリケーションの WebContent とほぼ同じです。Nexaweb では、このディレクトリのコンテンツは plugin.jar 構造のルートに配置されます。 |
/build | ビルドのステージング領域で、ビルド プロセスが終了すると削除される場合があります。 |
/dist | ビルドによって生成されるプラグイン JAR。 |
プラグインの配布には、次の表に示すディレクトリ構造が使用されます。
配布用ディレクトリ | 含まれる内容 |
/lib | プラグインが依存する JAR ファイル(クラスはクライアントにバインドされており、公に提供可能である必要があります)。 |
/images | イメージ。 |
/PLUGIN-INF/docs | プラグインドキュメントの場所。 |
/PLUGIN-INF/schema/plugin.xsd | プラグインのスキーマ ファイル。 |
/stylesheet/stylesheet.xss | プラグインのスタイルシート。 |
/plugin.xml | プラグインのマニフェスト ファイル。 |