本主题描述可扩展性 API,它允许通过在指定的时间间隔进行轮询、在测试执行后进行轮询以及订阅事件生成器来捕获事件。
本章包含:
Javadocs 在哪里?
可以通过选择 Parasoft> Help,然后打开 Parasoft SOAtest Extensibility API 书访问监视器工具 API 的 Javadocs。与事件监视工具直接相关的资源有 com.parasoft.api.IEvent
,它的默认实现适配器 com.parasoft.api.Event
,和 com.parasoft.api.EventSubscriber
可用模式
可扩展性 API 允许使用以下三种模式之一捕获事件:
在指定的时间间隔进行轮询
当试图监视系统中的事件与测试执行不同步时,此模式非常有用。例如,你可能对从日志框架中提取数据感兴趣,该框架以指定的时间间隔记录事件,因此你希望以相同的时间间隔在 SOAtest 中捕获已记录的事件,以确保实际检索到这些事件。
与此类似的是,某人是某一特定月刊的粉丝,但不是为了收到每一期已出版的杂志而订阅它,读者每月都会去书店购买最新一期。
这种模式的一个有趣之处在于,如果你在新事件可用之前对事件进行轮询,那么你可能会得到与上次轮询相同的事件,或者根本没有事件(这取决于你的目标框架的行为)。这就像我们的示例阅读器可能在商店中只找到上个月的一期,因为当前的一期还没有发布。
使用此模式,你可以指定一个时间量(以毫秒为单位),它将作为脚本执行之间的等待期。例如,如果将间隔指定为 1000 毫秒,则事件监视器工具将每 1 秒执行你提供的代码。这种情况将持续到发生以下两个事件之一:
- 如果事件监视器执行测试套件的一部分与其他各种测试,定期用户代码执行就会停止最后一个测试在测试套件(或者数据源中的最后一行,前提为:测试套件测试是遍历一个数据源)完成执行。
- 已达到最大监视执行持续时间(该值在 Options 选项卡下配置)。
在每次测试执行之后进行轮询
当试图监视系统中的事件通常与测试套件的测试执行事件同步时,此模式非常有用。例如,在系统中发生事件之后,系统的日志框架会立即被触发,并且这些事件可供你获取。
使用此模式,你提供的代码将在测试套件中的每个测试执行之后立即执行。如果你单独运行事件监控工具(除了执行包含它的整个测试套件之外),则此模式不适用。在这种情况下,事件监视器将停止一次:
- 测试套件中的最后一个测试(如果测试套件测试在数据源上迭代,则是数据源中的最后一行)已经执行完毕。
- 已达到最大监视执行持续时间(该值在 Options 选项卡下配置)。
订阅事件生成器
这可能是最常见的模式,Parasoft 建议尽可能使用它。当试图监视的框架允许订阅者在事件发生时立即被触发时,这种模式非常有用。换句话说,它可以执行“回调”功能。
例如,JMS 发布/订阅消息模式就是这种模式的一个例子,它用于驱动 Sonic、TIBCO 和事件监控工具中支持的其他内置平台。在我们的杂志阅读器示例中,这类似于订阅出版物,因此最新一期的杂志一出版就会被交付给读者。
使用“在指定的时间间隔进行轮询”和“在每次测试执行之后进行轮询”模式
当使用这两种模式时,你在 User Code 部分中选择的方法将按照各自的模式执行。你可以将方法名称与你希望的任何名称组合在一起(请确保 Method 菜单中选中了该名称)。
IEvent getEvent(String url, String username, String password, Object connection, ScriptingContext context)
- url (String): 事件监视器连接部分的 URL 字段中提供的值。
- usernameString): 事件监视器连接部分的 username 字段中提供的值。
- passwordString): 事件监视器连接部分的 password 字段中提供的值。
- connection(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 的实现。在本例中,我们返回 com.parasoft.api.Event,它是该接口的适配器实现,并接受一个简单的字符串对象“Hello!”
保持连接
在实践中,能够创建到你希望监控并重用该连接来检索事件的远程系统的连接(而不是在每次用户代码调用时创建新的连接),这一点常常很有用。因此,除了具有如上所述的检索 Ievent 对象的方法外,你还可以选择添加两个额外的可选方法;
Object createConnection(String url, String username, String password, com.parasoft.api.Context context)
Object destroyConnection(Object connection, com.parasoft.api.Context context)
createConnection 将创建一个对象句柄并将其返回到监视连接,而d estroyConnection 接收该对象,并允许你为优雅的断开连接提供代码。
事件监视器寻找这两个可选方法的存在。如果要添加它们,请确保使用正确的方法签名。 createConnection() 在事件监视器执行开始时调用一次,而 destroyConnection 在执行结束时调用一次。事件检索方法(例如上面的 getEvent())在按照所选模式运行事件监视器测试期间可能被调用多次。使用 createConnection 方法创建的连接对象被传递给事件检索方法,因此可以使用该连接返回事件。
例如
from com.parasoft.api import * def getEvent(url, username, password, connection, context): return connection.getEvent()
使用“订阅事件生成器”模式
使用此模式,事件监视器期望一个方法(具有任意名称,只要在 Method 下拉菜单中选择该名称),并且具有以下签名:
EventSubscriber getEventSubscriber(String url, String username, String password, Context context)
在前面的模式中有提供参数说明。
在本例中,你需要提供 EventSubscriber 的 Java 实现(通过从 com.parasoft.api.EventSubscriber 继承)。具体实现方法如下:
public boolean start() Throws Exception
和 public boolean stop() Throws Exception
事件监视器开始时将自动调用 start 方法,事件监视器执行结束时将自动调用 stop 方法。在此模式下的假设是,EventSubscriber 实现将负责连接到目标系统并在 start() 中订阅其事件生成框架,然后在 stop() 中取消订阅并断开连接。下面提供了订阅 TIBCO EMS 消息监视主题的示例实现。这实际上反映了事件监视器内置的 TIBCO EMS 平台所使用的模式。
例如
在与 SOAtest 一起发布的 examples 脚本目录下也可以找到这个例子。它作为 Eclipse 项目包含在 zip 归档文件中,可以导入到 Eclipse 工作区。它需要将 TIBCO EMS 和 com.parasoft.api.jar 中的 tibjms.jar 添加到类路径中,以便构建和运行。 com.parasoft.api.jar 可在 <SOAtest Installation Directory>\plugins\com.parasoft.xtest.libs.web_<SOAtest 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; } } }