Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Nexaweb Client

...

A typical use case involves including advanced charting capabilities in the Nexaweb Client. Although the client comes with some built in charting ability, it may not be sufficient to display certain advanced charts like those found in financial applications. Using the plug-in architecture, developers can integrate a 3rd-party chart component of their choice and define their own XML tags and format to display that chart. That chart can then be imbedded inside of standard NXML.

Example:

のプラグイン アーキテクチャにより、開発者は、独自のタグ、属性、および XML シンタックスを実装して Nexaweb Client の機能を拡張できます。一般に、開発者は新しい UI コンポーネントを使用して Nexaweb Client の機能を拡張しますが、論理演算、レイアウト マネージャ、またはその他のカスタム ロジックを実装することも可能です。

Nexaweb Client でプラグインがよく使用されるケースとして、高度なグラフ作成機能が必要な場合などがあります。Nexaweb Client にはもともとグラフ機能が備わっていますが、財務アプリケーションで見られるような、より高度なグラフを表示するには不十分な場合もあります。開発者は、 プラグインアーキテクチャを使用して、選択したサードパーティ製のグラフコンポーネントを統合し、そのグラフを表示するための独自の XML タグやフォーマットを定義できます。その後、このグラフを標準の NXML 内に埋め込むことができます。

例:

 

Code Block
<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>

Technical Overview

...

技術の概要

最 も基本的なレベルでは、プラグインは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

...

In the example above, the developer has some 3rd party charting components that serve as the peers. The handling classes that extend AbstractTagImpl are responsible for manipulating these peers based on changes in the XML, as well as responding to changes in the peer classes.

AbstractTagImpl and its subclasses automatically listen for StructureChangeEvents and AttributeChangeEvents their associated XML elements. It is the responsibility of the individual subclasses to perform some meaningful operation when attributes change or child tags are added and removed. Similarly, subclasses can listen for changes to a peer object and fire events or update the UI Document Object Model (DOM) based on those changes. The bridge class is a two way bridge in that is listens for XML changes and passes them on to the peer, and listens for peer changes and fires events or updates the DOM based on those changes.

Care should be taken to ensure that plug-ins will run in the desired JVMs. A UI component plug-in based on Swing will only run on Swing-compatible (1.2+) JVMs.

Developing a JButton Plug-in

This section develops a simple plug-in that will map the <jButton> tag to a JButton component. The <jButton> tag will understand basic attributes such as bgColor. In addition the <jButton> tag will implement an attribute named "armed" as a boolean, and fire onCommand events when activated.

...

クラスを実装することも可能です。

上記の例では、開発者は、ピアとして機能するサードパーティ製のグラフ作成コンポーネントを使用します。AbstractTagImpl を拡張する処理クラスには、ピアクラスへの変更に応答すると共に、XML への変更に基づいてこれらのピアを操作する役割があります。

 

Code Block
languagejava
linenumberstrue
public class JButtonBridge extends SwingBridge {
    public void createUiComponent(){
          setUiComponent(new JButton());
    }
}

At this point, our bridge class will create a JButton component, and will automatically respond to basic attributes like bgColor. We would also like this tag to respond to the "armed" attribute. We can do this by overriding onAttributeSet() to handle the new attribute.  こ の時点で、ブリッジクラスは JButton コンポーネントを作成して、bgColor などの基本属性に自動的に応答します。また、このタグを "armed" 属性に応答させる必要があります。そのためには、新しい属性を処理するために onAttributeSet() をオーバーライドします。 

Code Block
languagejava
linenumberstrue
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 is a utility class that provides methods that convert Strings to booleans, Color, enumerations and other data types. Overriding attributeSet and using AttributeConverter makes any errors in the XML report in the usual manner.Now that our <jButton> can respond to an attribute change, we would also like the <jButton> to report "onCommand" events when the JButton ActionListener is called. We do that by implementing ActionListener and adding ourselves as a listener to the JButton. Then when actionPerformed() is called we simply call a method from AbstractTagImpl that handles the firing of the event. The following is the final code for JButtonBridge with the most recent changes highlighted.は、文字列をブール型、Color、列挙型、その他のデータ型に変換するメソッドを提供するユーティリティクラスです。attributeSet をオーバーライドしてAttributeConverter を使用すると、XML でのすべてのエラーが通常の方法で報告されます。

<jButton> が属性の変更に応答できるようになったので、今度はJButton ActionListener が呼び出されたときに、<jButton> が "onCommand" イベントを報告する必要があります。そのためには、ActionListener を実装して、それ自体をリスナとして JButton に追加します。その後で、actionPerformed() が呼び出されたら、イベントの発生を処理する AbstractTagImpl からメソッドを呼び出すだけです。次に、JButtonBridge の最終的なコードを示します。最近行われた変更内容がハイライトされています。

Code Block
languagejava
linenumberstrue
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);
    }
}

Now that our JButtonBridge is complete we must add a mapping so that our <jButton> tag is defined and handled by the JButtonBridge class. To do this we must create a tag mapping file with the appropriate entry, then add that file to our list of tag mapping files in the client configuration. The tag mapping file itself is simply a list of tag names with the name of the handling class as the text. We will call this file customTagMappings.xml:JButtonBridge が完了したので、今度は<jButton> タグがJButtonBridge クラスによって定義および処理されるようにマッピングを追加する必要があります。そのためには、適切なエントリを使用してタグマッピングファイルを作成す る必要があります。次に、クライアント構成内のタグマッピングファイルのリストにそのファイルを追加します。タグマッピングファイル自体は、テキスト形式 の処理クラス名を持つタグ名のリストに過ぎません。このファイルを customTagMappings.xml と呼びます。

Code Block
<tag-mappings>
   <jButton>JButtonBridge</jButton>
</tag-mappings>

Once this file is created we must add an entry to このファイルが作成されたら、エントリを nexaweb-client.xml :に追加する必要があります。

Code Block
<client-tag-mapping>
    <tag-map-file>customTagMappings.xml</tag-map-file>
</client-tag-mapping>

...

...

...

...

...

...

Plug-in Class Lifecycle

When a new NXML block is appended to the existing UI DOM:

  1. The name of the class associated with the root element is looked up.
  2. The class is loaded if not loaded already.
  3. A new instance of the handler class is created.
  4. setSession() is called on the handler with the current ClientSession.
  5. The handler is added to the NXML element as a StructureChangeListener and AttributeChangeListener.
  6. setElement() is called on the handler with the NXML element.
  7. init() is called on the handler.
  8. The "onCreate" event is fired on the handler.

This process is performed only once for the entire NXML block. The root element of that block may have children and/or initial attributes set on it. AbstractTagImpl provides two methods which can be called in init() to handle these situations. The "parseInitialChildren()" method will repeat this parsing process for all children of the initial element. The "parseInitialAttributes()" method will feed the attributes on the initial element through onAttributeSet() as if they were being set after the element was created. In addition, ContainerBridge will call these methods by default in its init() method.

When an NXML block is removed from the UI DOM:

  1. unload() is called on the handler for the root element.
  2. The handler is removed from the NXML element as a StructureChangeListener and AttributeChangeListener
  3. The handler calls afterChildRemoved(), which recursively repeats the process for each child element of the root Element being removed.

If a handler class override onChildRemoved(), it must be sure to call afterChildRemoved() to make sure the recursive clean-up takes place.

Packaging Plug-Ins

Plug-ins consists of three sets of resources: tag-mapping files, the tag-handler classes and the peer classes. In our example the JButton peer class is included in any Swing-compatible JVM, but often peer classes will come in separate JAR files.

...

  1. ルート要素に関連付けられているクラスの名前が検索されます。
  2. ロードされていないクラスがロードされます。
  3. ハンドラ クラスの新しいインスタンスが作成されます。
  4. setSession() が現在の ClientSession を使用して、ハンドラで呼び出されます。
  5. ハンドラが StructureChangeListener と AttributeChangeListener として NXML 要素に追加されます。
  6. setElement() が NXML 要素を使用して、ハンドラで呼び出されます。
  7. init() がハンドラで呼び出されます。
  8. "onCreate" イベントがハンドラで発生します。
このプロセスは、 NXML ブロック全体で一度だけ実行されます。このブロックのルート要素は、子または子に設定された初期属性、あるいはその両方を持つ場合があります。 AbstractTagImpl は、このような状況を処理するために、init() で呼び出し可能な 2 つのメソッドを提供します。"parseInitialChildren()" メソッドは、初期要素のすべての子に対してこの解析プロセスを繰り返し行います。"parseInitialAttributes()"メソッドは、要素が作成後、設定されたかのようにonAttributeSet()によって初期要素の属性をフィードします。それに加え、ContainerBridgeは、初期設定によりinit() method にこれらのメソッドを呼び出します。

NXML block はUI DOMから削除されます

:
  1. unload() はroot 要素に対しハンドラに呼び出されます。
  2. ハンドラは、StructureChangeListener と AttributeChangeListener として NXML要素から削除されます。
  3. ハンドラが、再帰的に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 のドキュメントご確認下さい。

 

Code Block
<!-- 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>

One final packaging option is to make plug-in classes load on startup, rather than when the tag corresponding to that plug-in is first encountered. Classes loaded at startup will load during the Nexaweb loading animation, whereas large plug-ins loaded on the fly can cause the Nexaweb application to freeze for a few seconds. If an application makes consistent and frequent use of a large plug-in loading it at startup is often the better choice. This can be done by modifying the contents of the tag-mapping file as below:パッケージングの最終的なオプションは、プラグインに対応しているタグが最初にエンカウンタする時より、起動時に プラグインクラスをロードさせることです。起動時にロードされたクラスは Nexaweb がアニメーションをローディングしている間ロードしますが、一方でオンザフライでロードされた大きなプラグインが原因で、Nexaweb アプリケーションが数秒フリーズする可能性があります。アプリケーションが常に大きいプラグインローディングを使用する場合、起動時にローディングするのが良い選択でしょう。これは、次のようにタグマッピングファイルのコンテンツを修正することによって行う事ができます :  

Code Block
<tag-mappings>
    <jButton loadPolicy="onStartup">JButtonBridge</jButton>
</tag-mappings>

Namespaces can be defined for a set of tags.  An example is the プラグイン名前空間の定義

名前空間はタグセットに対し定義されます。例は Nexaweb Data Framework , which was implemented using the plugin architecture.  The XML that defines this plugin isで、プラグインアーキテクチャを使用し実装しています。このプラグインを定義する XML は以下になります :

Code Block
<tag-mappings namespace="http://nexaweb.com/data" 
    document="bindings">
    <mapping class="com.nexaweb.plugin.data.bridge.BindingBridge" 
             name="binding"/>
</tag-mappings>

This declares the tag "binding" in the nexawebこれはnexaweb.com/data namespace.

Overriding Built-in Components

Plugins can be used to replace the behavior of built in NXML tags.  For instance if a plugin was defined as <button> it would replace the built in behavior of button.namespaceでタグ “binding” を宣言します。

ビルトインコンポーネントをオーバーライド

プラグインはビルトイン NXML タグの振る舞いを置換えるために使用されます。例えばプラグインが <button> として定義される場合、ボタンのビルトインビヘイビアを置き換えます。