プラグイン開発者ガイド

  1. プラグインとは
    1. プラグイン作成時の注意
  2. プラグインの種類
    1. プロパティエディタとは
    2. プロパティリスナーとは
    3. コンポーネントエディタとは
    4. 外部アプリケーション起動とは
  3. プロパティエディタの作成
    1. ButtonTextCellEditor
    2. ButtonCellEditor
    3. 実装例
    4. 定義ファイルへの組み込み
  4. プロパティリスナーの作成
    1. 実装例
    2. 定義ファイルへの組み込み
  5. MetaDataの実装
  6. コンポーネントエディタの作成
    1. 実装例
    2. 定義ファイルへの組み込み
  7. 外部アプリケーション起動の作成

1 プラグインとは

プラグインとはデザイナーの動作を拡張し、コンポーネントやマッパー関数のプロパティ設定などの補助をするモジュールのことです。
コンポーネントやマッパー関数のクラスがフローの実行時にサーバー側で動作するモジュールであるのに対し、 プラグインはフローの設計時にデザイナー側で動作するのでフローの実行には影響を与えません。
プラグインはコンポーネント/マッパー関数の作成で必ず作らなければならないというものではありませんが、 多機能なコンポーネント/マッパー関数を作成する場合プラグインを作成するとデザイナーの操作性が向上し、 フロー作成の効率が大幅にアップすることもあります。

1.1 プラグイン作成時の注意

プラグイン作成では多くのクラスを使用します。
それらのクラス群はサーバーで使用しているクラスと同一の場合もありますが、ほとんどの場合プラグインで使用する クラスとサーバーで使用するクラスは全く別物です。

たとえばプラグインでも「Component」というクラスを使用しますが、そのパッケージは

com.infoteria.asteria.flowbuilder2.component.Component

であり、サーバーで使用するクラス

com.infoteria.asteria.flowengine2.flow.Component

とは全く別のクラスです。
後者はサーバー上で実際にコンポーネントの行う処理を実行するクラスですが、 前者はデザイナーで使用されるxfpに保存されているコンポーネントの内容をラップしたクラスです。

2 プラグインの種類

現在作成可能なプラグインの種類は次の4つです。

以下にそれぞれのプラグインの概要を説明します。

2.1 プロパティエディタとは

コンポーネントやマッパー関数がどのようなプロパティを持っているかは、 定義ファイルのProperty要素で定義されます。

プロパティの型は定義ファイル内でtype属性によって示され、 int型のプロパティの場合は数値しか入力できす、 choice型のプロパティではドロップダウンリストからプロパティ値が選択できるようになっています。 またdate型やdatetime型のプロパティのように日付を選択するカレンダーが表示されるものもあります。

つまりプロパティの型によって値を設定するためのUIが決まっています。
そのプロパティ値を設定するためのUIがプロパティエディタです。

通常プロパティ型とプロパティエディタは1対1で対応していますが、 string型のようにeditor属性でプロパティエディタを選択できるものもあります。

string型のプロパティエディタ
editor属性値 プロパティエディタ
デフォルト(省略時) 1行テキストダイアログ
multiline 複数行テキストダイアログ
condition 条件式の補完機能つき1行テキストダイアログ

もっともわかりやすい例はBranchStartコンポーネントの条件式プロパティで使用されている 「condition」エディタでしょう。
このエディタは「$」や「.」をタイプした時に条件式の補完をしてくれます。
このようにプロパティ値を設定するための補助的な機能を付加したUIを自作できるプラグインが プロパティエディタです。

2.2 プロパティリスナーとは

プロパティリスナーはJavaプログラムで日常的に使用されるEventListenerの一種で、 プロパティ値の変更に反応するリスナーです。

プロパティ値の設定前後でイベントが発生するのでそこで何かしらの処理を行うことができます。

PropertyListenerの典型的な実装例としては、プロパティ値の変更に伴い別のプロパティの表示状態など をコントロールできるSimplePropertyControllerがあります。

2.3 コンポーネントエディタとは

コンポーネントエディタとは複数のプロパティ値やフィールド定義などをまとめて設定できるように したものです。
コンポーネントエディタを使用している例としては

などがあります。

2.4 外部アプリケーション起動とは

外部アプリケーション起動はその名のとおり外部アプリケーションを起動するためのプラグインです。
外部アプリケーション起動での処理の流れは以下のようになります。

  1. プロパティ値などのコンポーネント(マッパー関数)の設定情報をファイルに書き出す
  2. 外部アプリケーションを起動し1で書き出したファイルを編集
  3. 2で編集/保存された内容をコンポーネント(マッパー関数)に設定しなおす

外部アプリケーション起動を使用した例としてはExcelBuilder、PDFBuilderなどがあります。

3 プロパティエディタの作成

プロパティエディタを作成するためにはPropertyEditorインターフェース を実装したクラスを作成します。
このインターフェースはSwingのTableCellEditorインターフェースを拡張し、setPropertyメソッドを 追加したものです。

新しいPropertyEditorを作成する場合にインターフェースのメソッドをすべて自分で作成しなければ ならないケースはほとんどなく、通常は次のButtonTextCellEditorかButtonCellEditorを継承して作成します。

3.1 ButtonTextCellEditor

ButtonTextCellEditorはインスペクタの編集エリア内にテキストフィールドがあり、 その脇に「...」という小さなボタンがあるタイプのプロパティエディタです。
ボタンをクリックすることでそのプロパティ値を設定するための補助UI(ほとんどの場合はダイアログ) が起動します。

ButtonTextCellEditorを継承したPropertyEditorの例としては

などがあります。

ButtonTextCellEditorを作成する場合実装しなければならないメソッドはdoButtonActionメソッド のみであり、そこにボタンがクリックされた場合の処理を記述します。

3.2 ButtonCellEditor

ButtonCellEditorはインスペクタの編集エリア全体がボタンで覆われたタイプのプロパティエディタです。
つまりインスペクタ内で値を設定することはできず、編集エリアをクリックすることでそのプロパティ値を設定するための補助UI(ほとんどの場合はダイアログ) が起動します。

ButtonCellEditorを継承したPropertyEditorの例としては

などがあります。

ButtonCellEditorを作成する場合実装しなければならないメソッドはdoButtonActionメソッド のみであり、そこにボタンがクリックされた場合の処理を記述します。

3.3 実装例

ButtonTextCellEditorでもButtonCellEditorでもdoButtonActionメソッドの実装内容はほとんど同じになります。
典型的な例としてはダイアログを表示してそこでOKボタンがクリックされた場合にプロパティに値を設定します。

sampleフォルダに1行テキストダイアログの替わりに履歴機能つきComboBoxを使用するサンプル (HistoryPropertyEditor.java)があります。
以下にそのコードの中からdoButtonActionの部分のポイントを説明します。

(コード中の「_combo」は履歴機能付きのComboBox、「_file」は履歴を保存するFileです。
完全なコードはsampleフォルダ内にあるjavaファイルを参照してください。)

	/**
	 * ボタンが押された時の処理
	 * 履歴機能付きComboBoxの載ったダイアログを表示
	 */
	protected void doButtonAction(EventObject e) {
		String value = getProperty().getValueAsString();  //1
		_combo.setSelectedItem(value);
		
		Frame owner = PluginUtil.getApplicationFrame();   //2
		String title = getDialogTitle();                  //3
		MyDialog dlg = new MyDialog(owner, title);
		try {
			dlg.setLocationRelativeTo(owner);
			dlg.setVisible(true);
			if (dlg.isOK()) {                             //DialogがOKボタンで終わったか?
				value = (String)_combo.getSelectedItem();
				_combo.updateHistory();
				setCellEditorValue(value);                //4
				stopCellEditing();                        //5
				if (_file != null)
					_combo.saveToFile(_file);
			} else
				cancelCellEditing();                      //6
		} catch (IOException ex) {
			ex.printStackTrace();                         //7
		} finally {
			dlg.dispose();
		}
	}
  1. getPropertyメソッドで編集対象となるPropertyオブジェクトが取得できます。
    プロパティ値はPropertyオブジェクトのgetValueメソッド(返り値はObject)または getValueAsStringメソッド(返り値はString)で取得できます。

    getValueメソッドによって返されるオブジェクトはプロパティ型によって異なります。
    string型のプロパティではStringオブジェクトを返しますが、 int型のプロパティではLongオブジェクトが返されます。
  2. ダイアログを表示する際のOwnerFrameにはPluginUtil#getApplicationFrameの返り値を使用します。
    PluginUtilはプラグイン作成に有用なメソッドをまとめたユーティリティクラスです。
  3. 値の設定にはsetCellEditorValueメソッドを使用します。
    Propertyオブジェクトに直接setValue(またはsetValueAsString)メソッドで設定してはいけません。
    (Undoが正しく動作しなくなります。)
  4. 値を設定した場合はstopCellEditingメソッドで編集操作を完了します。
  5. 編集をキャンセルした場合はcancelCellEditingメソッドで編集操作を完了します。
  6. StackTraceは「DESIGNER_HOME/log/error.log」に出力されます。
    デバッグ文字列を出力したい場合はSystem.errに書き出せば同じくこのファイルに出力されます。
    (System.outへの出力は通常の方法では確認できません。)

3.4 定義ファイルへの組み込み

作成したプロパティエディタを定義ファイルに組み込むには対象のProperty要素にeditorClass属性で指定します。


<Property displayName="ファイルパス" name="FilePath" required="true" toolTip="FilePath" type="string" 
    editorClass="com.infoteria.asteria.flowbuilder2.plugin.HistoryPropertyEditor" filename="exe.txt"/>

HistoryPropertyEditorはStringを扱うプロパティエディタですので、対象となるプロパティはstring型 またはその派生型でなければなりません。
またここでは説明しませんでしたがMetaDataインターフェースを実装しているので、 追加の属性としてfilename属性を定義しています。
MetaDataインターフェースについては後述します。

4 プロパティリスナーの作成

プロパティリスナーを作成するためにはPropertyChangeListenerインターフェース を実装したクラスを作成します。
(ここでいうPropertyChangeListenerインターフェースはJavaBeansのPropertyChangeListenerではなく、 弊社独自のクラスです。)

このリスナーでは

の2つのイベントをハンドルすることができます。

propertyChangingではPropertyChangeVetoExceptionをthrowすることで、 プロパティ値の設定をキャンセルすることもできますので、 例えばプロパティ値の設定前に値をチェックして不正な場合はエラーとするようなプロパティリスナーを 作成することができます。

4.1 実装例

sampleフォルダに正規表現でプロパティ値をチェックして、 値が不正な場合にはエラーメッセージを表示した上で設定をキャンセルする サンプル(RegexCheck.java)があります。
以下にその完全なコードを示しポイントを説明します。

package com.infoteria.asteria.flowbuilder2.plugin;

import com.infoteria.gui.property.event.PropertyChangeEvent;
import com.infoteria.gui.property.event.PropertyChangeListener;
import com.infoteria.gui.property.event.PropertyChangeVetoException;
import com.infoteria.gui.util.MetaData;
import org.w3c.dom.Element;

/**
 * Property値の変更前に正規表現によるチェックを行うPropertyListenerです。
 */
public class RegexCheck implements PropertyChangeListener, MetaData, Cloneable {
	
	private static final String A_REGEX = "regex";
	
	private String _regex;
	
	//MetaData                                                 //MetaDataインターフェースについては次章で説明します。
	public void setup(Element el) {
		_regex = el.getAttribute(A_REGEX);
	}
	
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
	
	//PropertyChangeListener
	public void propertyChanged(PropertyChangeEvent e) {       //1
	}
	
	public void propertyChanging(PropertyChangeEvent e) throws PropertyChangeVetoException {
		Object o = e.getNewValue();                            //2
		if (o instanceof String) {
			String str = (String)o;
			if (str.length() == 0)
				return;
			if (!str.matches(_regex)) {
				PluginUtil.showError("Invalid value: " + str); //3
				throw new PropertyChangeVetoException();       //4
			}
		}
	}
	
}
  1. propertyChangedはプロパティ値の変更直後に発生するイベントです。
    ここでは何もしていません。
  2. 新しく設定された値を取得するにはPropertyChangeEvent#getNewValueメソッドを使用します。
    変更前の値を取得するにはPropertyChangeEvent#getOldValueメソッドを使用します。
  3. エラーダイアログを表示する場合はPluginUtil#showErrorメソッドを使用します。
    PluginUtilにはこの他に情報ダイアログや確認ダイアログを表示するためのメソッドも用意されています。
  4. プロパティ値の設定をキャンセルする場合にはPropertyChangeVetoExceptionをthrowします。

4.2 定義ファイルへの組み込み

作成したプロパティリスナーを定義ファイルに組み込むにはPropertyListener要素を追加し、 class属性でクラス名を、target属性で対象となるプロパティ名で指定します。


<!-- メールアドレス形式のチェック -->
<PropertyListener class="com.infoteria.asteria.flowbuilder2.plugin.RegexCheck" 
    target="From" regex="[a-zA-Z0-9_\.\-]+@[A-Za-z0-9_\.\-]+"/>

RegexCheckはMetaDataインターフェースを実装しているので、 追加の属性としてregex属性を定義しています。
MetaDataインターフェースについては次章で説明します。

5 MetaDataの実装

PropertyEditorやPropertyChangeListenerを実装したクラスがさらにMetaDataインターフェースを 実装していた場合、そのクラスでは追加の定義情報を定義ファイルから読み込めるようになります。

定義ファイルの解析時にそれが定義された要素を引数としてMetaDataインターフェースのsetupメソッド が実行されるのでその情報を読み出すことができるのです。

ここまでに説明したふたつのサンプルではいずれもMetaDataインターフェースを実装し、 追加の定義情報を要素の属性値から取得していました。
サンプルには現れませんでしたが、要素に子要素を定義してさらに複雑な構造体を定義情報として 持たせることも可能です。

MetaDataインターフェースではもうひとつcloneメソッドも実装する必要がありますが、 プラグインではほとんどの場合DeepCopyを行う必要がないので単純にCloneableを宣言するだけで大丈夫です。

これから紹介するコンポーネントエディタと外部アプリケーション起動のふたつのプラグインでは 基底となるクラスでMetaDataインターフェースが実装されているので、 setupメソッドをオーバーライドする場合はその先頭で「super.setup(el);」を実行しなければなりません。

6 コンポーネントエディタの作成

コンポーネントエディタはComponentEditorクラスのサブクラスとして作成します。
ComponentEditorは次のような処理が実装されています。

doActionメソッドはabstractメソッドとして定義されており、 プラグイン開発者がコンポーネントエディタの作成で実装しなければならないメソッドはこのメソッドのみです。

6.1 実装例

sampleフォルダのfcsample以下に標準のメールコンポーネントのプロパティをダイアログ上でまとめて 設定できるようにしたコンポーネントエディタのサンプルが入っています。


このサンプルコードには以下の処理が含まれています。

サンプルコードには現れませんがコンポーネントエディタでは他にも以下のようなことができます。

これらの処理の詳細な説明はここでは行いませんが興味のある方は御相談ください。

6.2 定義ファイルへの組み込み

作成したコンポーネントエディタを定義ファイルに組み込むにはListener要素を追加し、 class属性でクラス名を、menuItem属性で右クリックメニューに追加するキャプションを指定します。


<!-- メールアドレス形式のチェック -->
<Listener class="com.infoteria.asteria.sample.plugin.MailEditor" 
    menuItem="プロパティの編集"/>

このサンプルでは使用していませんが、setupメソッドをオーバーライドして 独自の定義項目を追加することも可能です。
(setupメソッドをオーバーライドする場合は先頭でsuper.setupを行ってください。)

7 外部アプリケーション起動の作成

外部アプリケーション起動はLaunchExternalAppクラスのサブクラスとして作成します。
単純にプロパティ値をファイルに保存してアプリケーションを起動するだけならサブクラスを作成せずに LaunchExternalAppクラスをそのまま使用することも可能です。
(定義ファイルリファレンスの外部アプリケーション起動を参照してください。)

外部アプリケーション起動はコンポーネントエディタの一種と考えることができます。
つまりコンポーネントエディタのdoActionメソッドの実装が以下の処理になっているものが外部アプリケーション起動です。

  1. preExecuteメソッドの実行
  2. 外部アプリケーションの起動
  3. postExecuteメソッドの実行

外部アプリケーションの起動中は以下のようなダイアログが表示され、デザイナーは外部アプリケーションが 終了するまでロックします。


LaunchExternalAppのサブクラスを作成する場合は通常はpreExecuteメソッドとpostExecuteメソッドを オーバーライドします。
preExecuteでプロパティ値などのコンポーネント(マッパー関数)のプロパティなどの情報をファイルに書き出し、 postExecuteで外部アプリケーションが更新したファイルを読み込んでそれを書き戻します。
プロパティ値の取得や設定の方法自体はコンポーネントエディタの場合と同じです。

外部アプリケーション起動のサンプルは現在ありませんがこちらも興味のある方は御相談ください。