このセクションでは、拡張 API について説明します。拡張 API は、指定された間隔でのポーリング、テスト実行後のポーリング、およびイベント プロデューサーのサブスクライブによってイベントを取得できます。

このセクションの内容:

Javadoc の場所

モニター ツールの API を参照するには、[Parasoft] メニューの [ヘルプ] をクリックし、[Parasoft SOAtest Extensibility API] を開きます。Event Monitor ツールに直接的に関連するリソースは、com.parasoft.api.IEvent、そのデフォルト実装アダプターである com.parasoft.api.Event、および com.parasoft.api.EventSubscriberです。

利用可能なパターン

拡張 API は、以下の 3 つのうちいずれかのパターンを使用してイベントをキャプチャできます。

指定された時間間隔でポーリング

このパターンは、モニター対象のシステムのイベントがテストの実行と同期していない場合に便利です。たとえば、特定の時間間隔でイベントをログに記録するフレームワークからデータを取得するため、確実にイベントを取得できるよう、SOAtest も同じ間隔でイベントを取得したい場合などです。

たとえ話で言うと、特定の月刊誌のファンが、その雑誌を購読して毎号を受け取るのではなく、毎月書店に行って最新号を買う場合に似ています。

このパターンで気を付けるべき点は、新しいイベントが利用可能になる前にポーリングを行うと、前回のポーリングで取得したのと同じイベントが取得されるか、1 つもイベントが取得されないことです。どちらになるかは、対象のフレームワークの動作によって異なります。これは先ほどの雑誌の読者のたとえで言うと、書店に行っても今月号がまだ発行されていないために、先月号しか見つからない状況に似ています。 

このパターンでは、スクリプトの実行間隔となる時間 (ミリ秒単位) を指定できます。たとえば、間隔を 1000 ミリ秒に指定した場合、Event Monitor ツールは、指定したコードを 1 秒ごとに実行します。この動作は、以下のいずれかのイベントが起きるまで継続されます。

  1. Event Monitor がほかに多くのテストを含むテスト スイートの一部として実行される場合、テスト スイート内の最後のテスト (テストがデータ ソースを繰り返し処理する場合は、データ ソースの最後の行) の実行が完了したとき、周期的なユーザー コードの実行が終了します。
  2. モニターの最大持続時間 (この値は [オプション] タブで設定します) が経過したとき。

各テストの実行後にポーリング

このパターンは、モニター対象のシステムのイベントがテストの実行とおおよそ同期している場合に便利です。たとえば、システムでイベントが発生した後、直ちにログ記録フレームワークがトリガーされ、イベントが取得できるようになる場合などです。

このパターンでは、テスト スイート内の各テストが実行された後、指定されたコードが直ちに実行されます。Event Monitor ツールを独立して (ツールを含むテスト スイート全体の実行とは別に) 実行している場合、このパターンは使用できません。このパターンでは、以下のいずれかの場合に Event Monitor が停止します。

  1. テスト スイート内の最後のテスト (テストがデータ ソースを繰り返し処理する場合は、データ ソースの最後の行) の実行が完了したとき。
  2. モニターの最大持続時間 (この値は [オプション] タブで設定します) が経過したとき。

イベント プロデューサーをサブスクライブ

このパターンはおそらく最も一般的であり、できるだけこのパターンを使用するよう推奨します。このパターンは、イベントが発生した直後に直ちにモニター対象のフレームワークがサブスクライバーをトリガーする場合、つまり「コールバック」関数を実行する場合に便利です。

たとえば、JMS の パブリッシュ/サブスクライブ メッセージ パターンは、このパターンの一例であり、Sonic や TIBCO などの Event Monitor ツールがサポートするプラットフォームで使用されているパターンです。雑誌の読者のたとえで言えば、最新号が発行されたら直ちに送付されるよう、雑誌の購読を申し込む場合に似ています。

“指定された時間間隔でポーリング” と “各テスト実行後にポーリング” パターンの使用

これら 2 つのパターンを使用する場合、[ユーザー コード] セクションで選択したメソッドは、それぞれのパターンに従って実行されます。メソッドには任意の名前を付けることができます ([メソッド] メニューでメソッドが選択されていることを確認します)。

IEvent getEvent(String url, String username, String password, Object connection, ScriptingContext context)

  • url (String): Event Monitor の [接続] セクションの [URL] フィールドで指定された値です。
  • username (String): Event Monitor の [接続] セクションの [ユーザー名] フィールドで指定された値です。
  • password (String): Event Monitor の [接続] セクションの [パスワード] フィールドで指定された値です。
  • connection(Object): Event Monitor の複数のスクリプト実行で同じ接続を保持して再利用する場合、任意オプションとしてこの object を指定できます。詳細については、「Maintaining Connections」 を参照してください。
  • context (com.parasoft.api.Context): SOAtest の標準的なスクリプト実行コンテキストであり、変数およびデータ ソース値へのアクセスや、複数のテスト実行間で共有されたオブジェクトの設定/取得を可能にします。

例 (Jython)

from com.parasoft.api import *
 
def getEvent(url, username, password, connection, context):
	return Event("Hello!")

基本的に、getEvent() メソッドのコードは、モニター対象のシステムからのイベント取得を制御し、com.parasoft.api.IEvent の実装を返します。この例では、このインターフェイスのアダプター実装であり、 単純な String オブジェクト“Hello!”を受け取る com.parasoft.api.Event を返しています。

接続の保持

実際の運用では、ユーザー コードの呼び出しのたびに新しい接続を作成する代わりに、モニター対象のリモート システムへの接続を作成し、イベントの取得のために接続を再利用できると便利な場合がよくあります。そのため、上で説明したように IEvent オブジェクトを取得するためのメソッドに加えて、以下の 2 つのメソッドを追加することもできます。

Object createConnection(String url, String username, String password, com.parasoft.api.Context context)

Object destroyConnection(Object connection, com.parasoft.api.Context context)

createConnection は、モニタリング接続へのオブジェクト ハンドルを作成して返します。destroyConnection は、同じオブジェクトを受け取り、適切に接続を切断するためのコードを記述できます。

Event Monitor はこれらの任意メソッドを探します。これらのメソッドを追加する場合、正確なメソッド シグニチャを使用してください。  createConnection() は Event Monitor の実行開始時に 1 回だけ呼び出され、destroyConnection は Event Monitor の実行終了時に 1 回だけ呼び出されます。上記の getEvent() などのイベント取得メソッドは、選択されたパターンに応じて、Event Monitor のテスト実行中に複数回呼び出される可能性があります。createConnection メソッドで作成された接続は、イベント取得メソッドに渡されるため、その接続を使用してイベントを返すことができます。

from com.parasoft.api import *
 
def getEvent(url, username, password, connection, context):
	return connection.getEvent()

“イベント プロデューサーのサブスクライブ” パターンの使用

このパターンでは、Event Monitor は次のシグニチャを持つ単一のメソッドを期待します ([メソッド] ドロップダウン メニューで選択されている限り、どのような名前のメソッドでも構いません)。

EventSubscriber getEventSubscriber(String url, String username, String password, Context context)

引数の説明は、前の 2 つのパターンと同じです。

このパターンでは、(com.parasoft.api.EventSubscriber を継承して) EventSubscriber の Java による実装を提供する必要があります。実装する必要のあるメソッドは以下のとおりです。

public boolean start() Throws Exception および public boolean stop() Throws Exception

start メソッドは、Event Monitor がモニターを開始するときに自動的に呼び出され、stop メソッドは、Event Monitor の実行が終了するときに自動的に呼び出されます。このパターンは、EventSubscriber の実装がターゲット システムへの接続を行い、start() メソッドでイベント プロデュース フレームワークをサブスクライブし、stop() メソッドでアンサブスクライブおよび接続の切断を行うことを前提としています。下の例は、TIBCO EMS のメッセージ モニタリング トピックにサブスクライブする場合の実装例です。これは、実際に Event Monitor がサポートする TIBCO EMS プラットフォームで使用されるパターンを反映しています。

この例は、SOAtest に付属のサンプル スクリプトのディレクトリにもあります。サンプル スクリプトは、Eclipse プロジェクトとして zip アーカイブで提供されており、Eclipse のワークスペースにインポートできます。スクリプトをビルドして実行するには、 TIBCO EMS の tibjms.jar および com.parasoft.api.jar がクラスパスに追加されている必要があります。com.parasoft.api.jar は <INSTALL>/plugins/com.parasoft.ptest.libs.web_<VERSION>/root ディレクトリにあります。

import com.parasoft.api.*; 
import com.tibco.tibjms.*; 
import javax.jms.*;
 
public class TIBCOEventSubscriber extends EventSubscriber {
    	private ConsumerRunnable _consumerRunnable;     
	private Connection _connection;
    	private String _dest;
    	private boolean _started = false;
	public TIBCOEventSubscriber(String url, String username, String password, String destination) throws JMSException {
		_dest = destination;
		QueueConnectionFactory factory = new TibjmsQueueConnectionFactory(url); 
		_connection = factory.createQueueConnection(username, password);
	}
	public boolean start() throws Exception {
		Session session = null;
		session = _connection.createSession(false, Session.AUTO_ACKNOWLEDGE); 
		Destination destination = session.createTopic(_dest); 
		MessageConsumer msgConsumer = session.createConsumer(destination);
		_connection.start();
		_started = true;
		_consumerRunnable = new ConsumerRunnable(msgConsumer);
		Thread thread = new Thread(_consumerRunnable);
		thread.start();
		Application.showMessage("Monitoring started on " + _dest);
		return true;
	}
 
	public boolean stop() throws Exception {
		_started = false;
		Thread.sleep(1000);
		if (_connection != null) {
			_connection.close();
			Application.showMessage("Monitoring connection closed");
		}
		if (_consumerRunnable != null && _consumerRunnable.getException() != null) {
			throw _consumerRunnable.getException();
		}
		return _started;
	}
 
	private class ConsumerRunnable implements Runnable {
		private MessageConsumer msgConsumer; 
		private Exception t;
 
		public ConsumerRunnable(MessageConsumer msgConsumer) {
			this.msgConsumer = msgConsumer;
		}
		public void run() {
			while(_started) {
				try {
					MapMessage msg = (MapMessage)msgConsumer.receive(500);
					if (msg == null) {
						continue;
					}
					Message actualMessage = null; 
					try {
						byte[] bytes = msg.getBytes("message_bytes"); 
						if (bytes != null && bytes.length > 0) {
							actualMessage = Tibjms.createFromBytes(bytes);
						}
					} catch (JMSException e1) {
					}
					// you can provide your own extension of Event in order to customize the
event
					// getLabel(), toString() and toXML() outputs 
					IEvent event;
					if (actualMessage == null) {
						event = new Event(msg);
					} else {
						event = new Event(actualMessage);
					}
					event.setTimestamp(msg.getJMSTimestamp());
					onEvent(event);
				} catch (JMSException e) {
					Application.showMessage(e.getClass().toString() + ": " + e.getMessage()); 
					try {
						msgConsumer.close();
					} catch (JMSException e1) {
						Application.showMessage(e1.getClass().toString() + ": " + e1.getMessage());
					}
					t = e;
					_started = false;
				}
			}
		}
		public Exception getException() {
			return t;
		}
	}
}



  • No labels