使用 Eclipse 和 Java 对 IBM Rational ClearQuest 进行二次开发
 

2009-02-06 作者:杨 卓,王 深 来源:IBM

 
本文内容包括:
IBM® Rational® ClearQuest 是业界领先的变更管理工具,提供 VBScript、Perl 语言编程接口进行二次开发。本文着重介绍使用 Eclipse IDE 和 Java 语言通过 JNI 调用对 ClearQuest 进行二次开发,更方便地与其他基于 Java 开发的企业级应用系统进行集成。

介绍

Eclipse 是开放源码社区,其项目专注于提供用于构建软件的可扩展开发平台和应用程序框架。IBM 于 2001 年建立了 Eclipse 联盟,并将 Eclipse 作为礼物送给开放源码社区。目标在于让开放源码社区控制代码,让联盟处理商业关系。联盟有 9 个初始成员,其中包括 IBM 合作伙伴和竞争对手。IBM 通过资助 Eclipse 创新基金等各种计划和主办 Eclipse 代码活动来不断促进平台的发展。现如今,Eclipse 已经成为业内领先的平台。

Rational ClearQuest 在 Rational 产品家族中,处于整个软件生命周期和测试周期的中心地位。Rational ClearQuest 是重要的变更管理工具,Rational ClearQuest 更能让客户根据自己的具体需求,灵活的设计变更管理流程。除此之外,Rational ClearQuest 还提供了一套编程接口,即 Rational ClearQuest API,让用户和开发人员可以根据需要通过编程来实现 Rational ClearQuest 的相关功能,同时还可以通过编程来实现模式中 Hook 脚本的开发和定制,从而使整个 Rational ClearQuest 产品的功能更加灵活和强大。

Java 作为一门通用语言广泛被运用于企业应用系统开发当中,其开发体系为企业级开发提供了很好的平台。读者可以通过 IBM developerWorks 中的 Java 专区获取更多 Java 企业级应用的知识(参见 参考资料)。

Rational ClearQuest API 支持 VBScript、Perl 语言开发接口。在实际应用中,为了更好地与企业级应用系统进行集成,可以使用 JNI 技术,使用 Eclipse 平台和 Java 语言进行 Rational ClearQuest 二次开发。

开发环境搭建与测试

读者可以通过 Rational 在线资源下载 IBM Rational ClearQuest 试用版。(参见 参考资料)双击安装程序,根据安装引导进行安装:

图 1. IBM Rational ClearQuest 安装向导
IBM Rational ClearQuest 安装向导

安装程序默认安装在系统盘,安装成功后查看安装目录,会有以下安装组件:

图 2. 安装组件
安装组件

下面搭建 Eclipse、Rational ClearQuest 开发环境。新建 Eclipse 工程,引入 Rational ClearQuest 安装目录下的 jqjni.jar 包。

图 3. jqjni.jar 包
jqjni.jar 包

为了证明环境搭建成功,编写测试代码,连接 Rational ClearQuest 并遍历所有 Entity 。操作步骤为:

  1. 通过 CQSession 连接 ClearQuest 服务器
  2. 通过 GetEntityDefNames 遍历 Entity

实现代码如下:

package com.ibm.developerworks;
import com.rational.clearquest.cqjni.CQException;
import com.rational.clearquest.cqjni.CQSession;
public class TestConnection {
private final static String USERNAME = "yangzdl@cn.ibm.com";
private final static String PASSWORD = "GIVEATRY";
private final static String DATABASE = "CQDATABASE";
public static void main(String[] args) {
CQSession _cqsession = new CQSession();
try {
_cqsession.UserLogon(USERNAME, PASSWORD, DATABASE, "");
System.out.println("begin to list clearquest entities");
if (_cqsession.GetEntityDefNames() != null) {
for (int i = 0; i < _cqsession.GetEntityDefNames().length; i++)
System.out.println(_cqsession.GetEntityDefNames()[i]);
}
} catch (CQException e) {
e.printStackTrace();
System.err.println("can't connect to clearquest server");
}
}
}

运行后有如下运行结果:

begin to list clearquest entities
Approval
Solution
TMSuiteLog
TMExternalFile
TMTestCase
ApprovalDelegation
UCM_Project
ToDo
users

测试程序成功输出了所有 Entity,说明环境搭建成功,已经可以使用 Eclipse 和 Java 通过 JNI 对 ClearQuest 进行操作了。

对比学习

ClearQuest 对象结构在 Java 语言中的映射

ClearQuest API 是面向对象设计的。通过 JNI 转换的 Java API 也是如此。下面表格很清楚地展现了 ClearQuest API 中对象在 Perl 和 Java 中的映射关系,帮助开发人员理解其内在联系。

Perl Java
CQAdminSession com.rational.clearquest.cqjni.CQAdminSession
CQDatabase com.rational.clearquest.cqjni. CQDatabase
CQEntity com.rational.clearquest.cqjni. CQEntity
CQEntityDef com.rational.clearquest.cqjni. CQEntityDef
CQFieldInfo com.rational.clearquest.cqjni. CQFieldInfo
CQHistory com.rational.clearquest.cqjni. CQHistory
CQQueryDef com.rational.clearquest.cqjni. CQQueryDef
CQQueryFieldDef com.rational.clearquest.cqjni. CQQueryFieldDef
CQResultSet com.rational.clearquest.cqjni. CQResultSet
CQSession com.rational.clearquest.cqjni. CQSession

通过对 ClearQuest 常用对象结构的比较,我们发现通过 JNI 转换的 Java API 与原来的 Perl API 是很相似的。在使用过程中,Perl API 可以直接调用对象,Java API 需要 new 一个对象。下面举例来说明。

使用 Perl API 登录 ClearQuest:

$sessionObj = CQSession::Build();
$sessionObj->UserLogon(“user_name”,”password”,”database_name”);

使用 Java API 登录 ClearQuest:

CQSession _cqsession = new CQSession();
_cqsession.UserLogon(“user_name”, “password”, “database_name”, "");

ClearQuest 主要常量在 Java 语言中的映射

在使用 ClearQuest API 进行开发的过程中,经常会使用一些常量,如查询操作符、结果返回码等。以 ClearQuest Perl API 查询为例:

@owner = ("yangzdl@cn.ibm.com"); 
@state = ("closed"); 
$queryDef = $CQsession->BuildQuery("defect"); 
@dbfields = ("ID","State","Headline"); 
foreach $field (@dbfields) { 
$queryDef->BuildField($field); 
} 
$operator=$queryDef->BuildFilterOperator($CQPerlExt::CQ_BOOL_OP_AND); 
$operator->BuildFilter("Owner", $CQPerlExt::CQ_COMP_OP_EQ,\@owner); 
$operator->BuildFilter("State", $CQPerlExt::CQ_COMP_OP_NOT_IN, \@state);

在增加查询操作符的时候,使用了 $CQPerlExt::CQ_BOOL_OP_AND,$CQPerlExt::CQ_COMP_OP_EQ 等。这些是 ClearQuest Perl API 中封装常量的方法,定义在了 CQPerlExt 中。在通过 Java 操作 ClearQuest 的时候,所有的常量都封装在了 com.rational.clearquest.cqjni.CQConstants 类中。下面一段代码说明如何在 Java 语言中如何使用 ClearQuest API 的常量,操作步骤如下:

  1. 通过 CQSession 连接 ClearQuest 服务器
  2. 通过 BuildQuery 建立查询描述
  3. 调用 BuildFilterOperator 建立查询条件
  4. 通过 BuildResultSet 获取查询结果
  5. 遍历 CQResultSet 得到结果集
public static void main(String[] args) {
CQSession _cqsession = new CQSession();
try {
_cqsession.UserLogon(USERNAME, PASSWORD, DATABASE, "");
CQQueryDef def = _cqsession.BuildQuery("defect");
def.BuildField("id");
def.BuildField("State");
def.BuildField("Headline");
CQQueryFilterNode filternode = def.BuildFilterOperator(CQConstants.CQ_BOOL_OP_AND);
filternode.BuildFilter("Owner", CQConstants.CQ_COMP_OP_EQ,new String[] 
	{ "yangzdl@cn.ibm.com" });
filternode.BuildFilter("State", CQConstants.CQ_COMP_OP_NOT_IN,new String[] { "closed" });
CQResultSet rs = _cqsession.BuildResultSet(def);
rs.Execute();
while (rs.MoveNext() == CQConstants.CQ_SUCCESS) {
CQEntity entity = _cqsession.GetEntity("defect", rs.GetColumnValue(1));
System.out.println("defect is : "
+ entity.GetFieldValue("id").GetValue() + "\t,"
+ entity.GetFieldValue("State").GetValue() + "\t,"
+ entity.GetFieldValue("Headline").GetValue());
}
} catch (CQException e) {
e.printStackTrace();
}
}

实战

通过 ClearQuest Java API 的学习,下面使用 Eclipse 和 Java 编写一个自定义的饼图报表生成工具。报表生成有两个部分。第一、获得数据,第二、生成报表。首先根据自定义条件,获得统计数据:

// 通过状态统计
private static int sumByDefectState(CQSession _cqsession, String _chart_field, 
    String _chart_operator,String _chart_condition, CQQueryDef _def , 
    CQQueryFilterNode _filternode,String _section_id) throws Exception {
// 建立查询
CQQueryDef def=_cqsession.BuildQuery("defect");
def.BuildField("id");
CQQueryFilterNode filternode= def.BuildFilterOperator(CQConstants.CQ_BOOL_OP_AND) ;
if(_chart_operator.equalsIgnoreCase("chart_in"))
filternode.BuildFilter(_chart_field, CQConstants.CQ_COMP_OP_EQ, 
    new String[]{_chart_condition});
// 查询结果集
CQResultSet rs=_cqsession.BuildResultSet(def) ;
tempConditionID = getConditionID(_section_id);
for (int _condition=0; _condition<tempConditionID.length; _condition++)
{
tempCondition = getCondition(_section_id, tempConditionID[_condition]);
tempCondition1=new String[1];
tempCondition1[0] = tempCondition[4];
// 根据自定义符号,进行查询条件匹配
if (tempCondition[3].compareToIgnoreCase("=") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_EQ, tempCondition1);
// 匹配不等于
else if (tempCondition[3].compareToIgnoreCase("<>") == 0 || 
tempCondition[3].compareToIgnoreCase("!=") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_NEQ, tempCondition1);
// 匹配大于
else if (tempCondition[3].compareToIgnoreCase(">") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_GT, tempCondition1);
// 匹配大于等于
else if (tempCondition[3].compareToIgnoreCase(">=") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_GTE, tempCondition1);
// 匹配小于
else if (tempCondition[3].compareToIgnoreCase("<") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_LT, tempCondition1);
// 匹配小于等于
else if (tempCondition[3].compareToIgnoreCase("<=") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_LTE, tempCondition1);
// 匹配 like 
else if (tempCondition[3].compareToIgnoreCase("like") == 0)
{
if(tempCondition[4].indexOf("%")!=-1)
tempCondition1[0]=tempCondition[4].replaceAll("%", "");
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_LIKE, tempCondition1);
}
// 匹配 between
else if (tempCondition[3].compareToIgnoreCase("between") == 0)
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_BETWEEN, tempCondition1);
// 匹配 in
else if (tempCondition[3].compareToIgnoreCase("in") == 0)
{
String inList=tempCondition[4];
StringTokenizer st=new StringTokenizer(inList,",");
tempCondition1=new String[st.countTokens()];
int _i=0;
while(st.hasMoreTokens())
{
tempCondition1[_i]=st.nextToken().trim();
_i++;
}
filternode.BuildFilter(tempCondition[2], CQConstants.CQ_COMP_OP_IN, tempCondition1);
}
}
// 执行查询
rs.Execute() ;
int sum=0 ;
// 对查询结果进行统计
while (rs.MoveNext() == CQConstants.CQ_SUCCESS)
sum++ ;
return sum ;
}

第二步,根据获得的数据生成报表。根据不同的状态返回的统计值,使用 jfreechart 生成饼图报表。参考实现代码:

// 生成饼图报表
private static void extractChart(CQSession _cqsession, StringBuffer sb,
    CQQueryDef def, String title, String chart_field,String chart_operator, 
    String chart_condition,CQQueryFilterNode _filternode, 
    String _section_id) throws Exception {
sb.append(addHtmlLet("hr", ""));
sb.append("<h3><font color=blue align=center><b>"+title+"</b></font></h3>");
sb.append("<hr />");
if(IS_DEBUG) log("=======================================begin to chart") ;
def.BuildField("id");
// 解析报表中统计的不同状态值
StringTokenizer st=new StringTokenizer(chart_condition,",");
String[] chart_str=new String[st.countTokens()];
int _i=0;
while(st.hasMoreTokens())
{
chart_str[_i]=st.nextToken().trim();
_i++;
}
// 初始化饼图
DefaultPieDataset dataset= new DefaultPieDataset();
for(int i =0 ; i<chart_str.length ; i++)
{
// 调用 sumByDefectState 获取状态值对应的统计数
int _t=sumByDefectState(_cqsession,chart_field,chart_operator,chart_str[i],
    def,_filternode,_section_id) ;
// 在饼图中加入状态统计结果
dataset.setValue(chart_str[i]+" "+String.valueOf(_t),_t);
}
// 生成饼图
JFreeChart chart = ChartFactory.createPieChart3D(title, // chart title
dataset, // data
true, // include legend
true,
false
);
try {
File f=File.createTempFile("dre_cq_chart", "jpg");
f.deleteOnExit();
// 将饼图报表以 JPEG 格式写入文件中
org.jfree.chart.ChartUtilities.saveChartAsJPEG(f, chart, 600, 300);
URL picurl = f.toURL() ;
String cid = cqmail.embed(picurl, f.getName());
// 将饼图报表嵌入
sb.append(addHtmlLet("br","")) ;
sb.append(addHtmlLetWithAttr("img","","src=cid:"+cid));
sb.append(addHtmlLet("br","")) ;
sb.append(addHtmlLet("br","")) ;
} catch (Exception e) {
e.printStackTrace();
}
sb.append("<hr /><br />");
}

运行结果如下:

图 4. 运行结果
运行结果

与其他企业系统进行集成

通过实战演示成功地获得了饼状图报表,可以将其运行于 IBM WebSphere Application Server 之中,与其他企业系统进行集成。在与其他企业系统集成的过程中通过 J2EE 容器的集中管理来实现不同系统间的通讯。

在集成过程中,实施者需要考虑不同系统之间的通讯方式(采用内存共享,webservice,还是远程方法调用 rmi,ejb 等等)、部署方式(不同的 jvm,还是相同的 jvm;不同的 classloader 还是相同的 classloader 等等)。根据实际情况选择相应策略,进而最优地实现 J2EE 与 IBM Rational ClearQuest 的集成。

总结

本文介绍了 IBM Rational ClearQuest 的开发接口,并展现了如何运用 Eclipse 和 Java 语言通过 JNI 对 IBM Rational ClearQuest 进行二次开发,生成饼图报表。希望通过本文的阅读,您可以使用 Eclipse 平台和 Java 更方便地将 IBM Rational ClearQuest 集成到现有企业信息系统之中。

参考资料

学习 获得产品和技术 讨论

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织