技术开发 频道

Jazz平台的扩展实践—EasyInspect插件

【IT168 专稿

    本文是Rational软件技术征文大赛(http://tech.it168.com/focus/200903/rationalgame/index.html)三等奖获奖作品。

    当被问到“你知道Jazz,你在使用Jazz吗”这个问题时,我相信很多人都会不假思索地说“是的”。但如果问题换成“你在Jazz平台的基础上开发过新工具吗”,大家是否还会给出肯定的回答呢?

    在Jazz平台席卷全球的同时,大家对Jazz的了解和喜爱越来越深,更多的人开始尝试基于Jazz的开发,以求对Jazz更深的了解。我们也是其中的一份子,并且成功实现了基于Jazz的用于代码检查的插件EasyInspect。这里,希望能够跟大家一起分享我们在开发EasyInspect过程中所获得的实践经验,并希望它能为其他Jazz热爱者带来一定帮助。

    首先,简单介绍一下我们的成果EasyInspect。前面已经提过,它是基于Jazz平台的插件,可以很好的与Rational Team Concert集成,它的目标是方便开发人员进行高效的代码检查,从而提高代码的质量。它主要的特性有:

    · 基于上下文的代码评论:自动记录被评论代码段的位置信息;源文件及被评论代码段的智能定位。

    · 与版本控制系统的集成:保证正确版本的源文件被检查;代码评论与版本信息保持关联。

    · 高效的通信:借用了Jazz平台的通信机制,使代码作者可以及时了解到新的评论;方便开发组基于特定的代码评论发起讨论。

    在开发EasyInspect的具体实践中,我们与Jazz的多个特性进行了亲密的接触。在接下来的部分,将从如下几个方面来介绍我们的实践,包括开发环境的搭建、工作项(WorkItem)的应用与扩展、版本控制与应用、工作流、与即时交谈工具的集成、插件的部署等。

1.配置基于Jazz RTC的扩展开发环境

    注册Jazz.net帐号

    在开始之前,先确保你有一个Jazz.net帐号。Jazz.net帐号不仅是下载代码的必要前提,它还允许你访问Jazz.net网上的各种开发资源,包括WIKI、论坛和整个开发社区。

    请访问https://jazz.net/首页注册Jazz.net帐号。

    下载Jazz RTC代码

    为了配置Jazz RTC开发环境,首先需要下载Jazz RTC的客户端和服务器端代码,我们同时需要可执行代码和源代码。

    请访问https://jazz.net/downloads/RationalTeamConcert找到下载页面。到写稿时Jazz RTC的最新版本是1.0.1.1。在“All Downloads”标签下找到下面的下载内容。

    · 下载可执行代码

    Express-C,Client for Eclipse IDE and Server (Zip版)

    · 下载源代码(在下载页面底部)

    - Jazz Team Server

    - Rational Team Concert Client

    - Jazz JUnit Tests

    解压缩成开发环境

    将Client for Eclipse IDE and Server(Zip版)解开到文件系统,下文以[InstallDir]指代解压缩的位置。由于Zip中的目录结构非常深,为了确保解压缩后的文件路径不超过文件系统的一些限制,请尽可能在系统根目录(或浅层目录)解压缩。

    解开后的内容结构大致如下所示。

[InstallDir]
   jazz
       buildsystem
       client
       repotools
       scmtools
       server

    接着解压缩源代码Zip文件。它们应当被解开到[InstallDir]/jazz/client/eclipse的位置。之后,文件系统中应该出现下面的目录结构。
[InstallDir]
   jazz
       client
           eclipse
               source
                   base
                   eclipse
                   rtc
                   server
                   tests

    到此为止,你已经准备好了Jazz RTC插件扩展的开发环境。你可以在在下面的位置找到Rational Team Concert的启动文件:

[InstallDir]/jazz/client/eclipse/TeamConcert.exe

    这将是你之后的开发环境。

    启动RTC和配置Target Platform

    启动RTC开发环境([InstallDir]/jazz/client/eclipse/TeamConcert.exe),并在Preference中(Window > Preferences... > Plug-in Development > Target Platform)配置插件开发的Target Platform为Jazz Team Server and Binaries。

    然后点击“Load Target”按钮,并在完成后确认。

    启动和调试客户端的Jazz插件

    到这里,Jazz客户端的开发环境已经配置完成。你可以像开发一般Eclipse插件一样开发Jazz RTC客户端的插件了,因为Jazz RTC插件首先也是一个Eclipse插件。

    运行和调试Jazz RTC插件的方法和普通Eclipse插件并无不同,可以在Run/Debug Configuration中(Run > Open Run/Debug Dialog…)配置。例如下图就是一个运行配置,加载了Workspace中一些RTC插件运行和调试。

    启动和调试服务器端的Jazz插件

    Jazz与传统的IDE最大区别就在于它拥有统一的服务器端,因此开发Jazz RTC扩展的一个重要内容是开发服务器端的扩展插件。为了方便服务器端的开发和调试,有必要配置一个能直接在IDE中启动和调试的Jazz服务器。

    之前我们已经下载和安装了Jazz Server的可执行代码,现在需要做的就是在IDE中建立一个运行配置来启动它。Jazz开发社区中有预先配置好了运行配置,我们可以直接下载使用。

    https://jazz.net/wiki/pub/Main/JazzServerRunAndDebug/JazzServer-06-RTC10-Maximal.launch

    下载上面的launch文件,并复制到Workspace中的任意项目里(一般launch运行配置被放在项目顶层的launches目录中)。刷新Workspace,就可以在Run/Debug Configuration中(Run > Open Run/Debug Dialog…)找到Jazz Server的运行配置。

    勾选Workspace中你开发的服务器端插件,之后启动就可以运行和调试Jazz Server了。

    (若遇到“org.apache.commons.logging.LogConfigurationException: Invalid class loader hierarchy. You have more than one version of 'org.apache.commons.logging.Log' visible, which is not allowed.”,请看下面关于org.apache.commons.logging的问题的解决办法。)

    注意:已知关于org.apache.commons.logging的问题

    有些情况下,会在启动Jazz Server时遇到关于logging的异常“org.apache.commons.logging.LogConfigurationException”。这时打开运行配置,并在Target Platform下寻找org.apache.commons.logging,会看到类似下面,同时存在1.0.4和1.0.5两个不同版本的情况:

    由于Eclipse的一个缺陷,如果你禁用其中一个,下次启动时它又会被自动启用。解决的办法是:

    (1)在Target Platform下面禁用所有的logging实现。

    (2)导入org.apache.commons.logging(1.0.5.jazz)到Workspace中(从菜单选择File > Import... > Plug-ins and Fragments)。

    (3)在运行配置的Workspace组中勾选org.apache.commons.logging(1.0.5.jazz)。

    然后重新启动运行配置,问题应该解决了。

2.Work Item的应用和扩展

    在团队协同工作中,常常需要记录、追踪和管理团队成员的各种工作和任务。这些工作和任务总是具有一些共同的属性,同时又相互有所区别;它们还具有若干种状态信息,进而和流程绑定。Jazz为管理这些工作和任务提供了工作项(Work Item)的概念:

    · 每个Work Item都有一个类型属性,它将Work Item按照使用的目的不同分为若干类,如Defect,Task等。如果Jazz中存在的几种Work Item类型不能满足需要,开发者可以通过扩展来加入新的Work Item类型。具有相同类型的Work Item将具有相同的属性集以及相同的状态转换策略。

    · Work Item有一组内建的属性,这些属性与它们的类型无关。也就是说每种Work Item都具有这些属性,如ID、摘要、描述、类型、状态、严重程度、优先级、创建时间、创建人等。除了内建属性外,每个Work Item类型都可以自定义新的属性,这些新的属性被称为定制属性。

    · 为了记录Work Item与其它Work Item之间,或与其它实体之间的关系,Jazz使用了Link的概念。已有的Link类型有“Parent”,“Child”,“Duplicate”等,分别表示一个Work Item是另一个的父任务、子任务,及重复项。Link信息并不是存储在Work Item之中,而是通过Link service提供的API进行操作。Link允许扩展,开发者可以自己的需要来定义所需的Link类型。

    Jazz支持对WorkItem的扩展以满足特定的需求,接下来我们结合在开发EasyInspection的过程中的实践,来具体介绍如何对Work Item进行扩展。

    在EasyInspection中,我们需要为用户创建一种特殊的、与Code Inspection相关的Work Item。Jazz中已有的类型,如Defect、Task等类型都不是很合适,而且我们希望通过该类型可以将这种Work Item与其它的Work Item区分开来,于是我们决定建立一种称之为Inspection的新的Work Item类型。

    Jazz为扩展Work Item类型提供的扩展点是“com.ibm.team.workitem.common.workItemType”。我们新建了一个Jazz/Eclipse插件项目,在plugin.xml中声明对它进行扩展,并指定新类型的ID、名称和分类,如下所示。

<extension
    point="com.ibm.team.workitem.common.workItemType">
    <workItemType
        id="com.ibm.team.rtc.inspection.common.workItemType.inspection"
        name="Inspection"
        category="com.ibm.team.workitem.workItemType"
    >
    <alias name="inspection" />
    </workItemType>
</extension>

    (注意,在运行EasyInspection之前,需要将该插件部署到RTC的Server中。)

    在运行时,通过所声明的类型ID“com.ibm.team.rtc.inspection.common.workItemType.inspection”找到WorkItem类型实例后,就可以动态的创建属于该类型的Work Item了:

    public static final String CodeInspectionWorkItemTypeID =
            "com.ibm.team.rtc.inspection.common.workItemType.inspection";
        /**
        * Create a "Inspection" work item instance, and then use the "workItemOperation" to initialize * it.
        * @param projectArea the handle of the project area;
        * @param workItemOperation a WorkItemOperation implementation that would initialize and
        * save the new work item;
        * @param monitor
        * @return the handle of the new created work item.
        * @throws TeamRepositoryException
        */
        public IWorkItemHandle createCodeInspectionWorkItem(IProjectAreaHandle projectArea,
            WorkItemOperation workItemOperation, IProgressMonitor monitor) throws TeamRepositoryException {
            // find the definition of the work item type
            IWorkItemType workItemType = findWorkItemType(projectArea,
            CodeInspectionWorkItemTypeID, monitor);
            // call workItemOperation to create, initialize and save the work item.
            return workItemOperation.run(workItemType, monitor);
        }
        private IWorkItemType findWorkItemType(IProjectAreaHandle prjArea, String itemTypeId,
            IProgressMonitor mon) throws TeamRepositoryException {
            // get the repository
            ITeamRepository repos = context.teamRepository();
            // get the instance of the work item client
            IWorkItemClient workItemClient = (IWorkItemClient) repos.getClientLibrary(IWorkItemClient.class);
            // find the work item type definition through the type ID.
            return workItemClient.findWorkItemType(prjArea, itemTypeId, mon);
        }

    当用户创建一个Inspection类型的Work Item时,往往需要指定与此Work Item相关联的代码片段或者文件名。例如,用户A发现在文件abc.java的50行处有一个编程错误,需要通知用户B修改。于是他创建一个类型为Inspection的Work Item给B,加入描述,并在Work Item中标注出此位置。为了记录该位置信息并帮助用户快速定位,我们决定扩展并使用Link,以满足此需求。

    与扩展Work Item类型一样,需要首先在plugin.xml中声明此扩展。Jazz提供的扩展点是“com.ibm.team.repository.common.linkTypes”,我们指定新的Link类型的ID为“com.ibm.team.rtc.inspection.common.linkType.sourceCode”,它是从一个Work Item指向一个或多个源代码文件。

<extension
    point="com.ibm.team.repository.common.linkTypes">
    <linkType constrained="false"
        id="com.ibm.team.rtc.inspection.common.linkType.sourceCode"
        internal="false">
        <source>
            <endpoint displayName="Work Item" id="workItem" multiplicity="0..1">
                <itemReferenceType itemTypeName="WorkItem"
                    packageURI="com.ibm.team.workitem">
                </itemReferenceType>
            </endpoint>
        </source>
        <target>
            <endpoint displayName="Source Code"
                id="sourceCode" icon="/icons/obj16/resolutn_wontdo.gif"
                multiplicity="0..n">
            </endpoint>
        </target>
    </linkType>
</extension>

    有了此声明后,在程序中可以通过此ID来获取到对该Link的描述符,然后通过Link service的API创建Link。

    // get the work item
    IWorkItem workItem = workingCopy.getWorkItem();
    .... ...
    // SourceCodeLocator contains the location information of the selected source files.
    SourceCodeLocator[] resources;
    ... ...
    // get the definition of the link type, and then get the descriptor
    // for it's endpoint
    IEndPointDescriptor endPoint = ILinkTypeRegistry.INSTANCE.getLinkType(
    “com.ibm.team.rtc.inspection.common.linkType.sourceCode “) .getTargetEndPointDescriptor();
    // save the location information one by one
    for (int i = 0, n = resources.length; i < n; i++) {
        // create a reference to the source code entity
        IURIReference uri = IReferenceFactory.INSTANCE.createReferenceFromURI(resources[i].toUri());
        // add this reference to the work item;
        workingCopy.getReferences().add(endPoint, uri);
    }

    下图为我们在EasyInspect中创建的LINK的具体实现效果:

3.版本控制与应用

    Jazz的内嵌版本控制组件提供诸如存储、获取、代码及文档共享等一系列功能来协助项目组的开发。大家对Jazz中的流以及变更集应该是比较了解,因此不再对此再多加叙述。这里要跟大家分享的是EasyInspect中与文件版本相关的一些实现。

    EasyInspect中将对代码的评论及当前所检查的源代码的版本信息进行了关联,因而保证了开发人员在查看并解决代码评论时可以准确地理解,避免了代码评论显示在不同版本的源代码中引起的误解。

    在具体实现当中,我们通过获取源文件对应的UUID来区分版本,并将该UUID信息存储在代码评论相关的工作项当中。

    IResource res;
    IShareable s = (IShareable) res.getAdapter(IShareable.class)
    IVersionableHandle versionHandler = s.getVersionable();
    String version = versionHandler.getStateId().getUuidValue();

    此外,通过该UUID,我们可以获取与之对应的完整的文件内容。因而如果开发人员本地的代码版本与被检查的代码的版本不一致时,EasyInspect可以自动获取到正确版本的源代码并以只读形式呈现给开发人员,从而使开发人员能够更好的理解对代码的评论。

    请大家注意下图标记中末尾处的字符串,这就是被获取到的UUID:

4.与即时交谈工具的集成

    Rational Team Concert现在支持与Lotus Sametime 7.5和Lotus Sametime 8.0的集成。用户可以很方便地在Rational Team Concert中和团队其他成员进行实时的交流。

    配置Rational Team Concert与Sametime的集成

    (1)在Sametime的安装目录,在配置文件plugin_customization.ini中,设置以下属性:com.ibm.collaboration.realtime.brokerbridge/startBroker=true

    (2)在Rational Team Concert中,选择WindowsàPreferencesàInstant Messaging,弹出的新窗口中设置新的即时通讯的账号(如下图)。

图 在Rational Team Concert中配置即时通讯账号

    在EasyInspect中,我们也借用了对Sametime的集成以方便组员之间的交流与协作。

5.WorkItem与工作流

    WorkItem可以用来跟踪开发过程中的问题和任务,并且是评估项目健康状况的重要因素。Rational Team Concert预定义了多个WorkItem类型,涵盖了计划、需求、用例、任务、构建和缺陷等方面。你还可以通过定义新的WorkItem类型以满足团队特别的流程需要。每一种WorkItem类型都会关联一个状态转移模型,在其中定义了该WorkItem类型可能的状态,在某一状态时用户可以采取的行为以及该行为所引起的状态的变迁。

    你可以通过以下步骤添加新的WorkItem类型:

    (1)在Team Artifacts视图中,右键Project Area,选择Open。选择Process Configuration,展开Project Configuration->Configuration Data->Work Items->Types and Attributes。

    (2)在Work Item Types区域中,点击add,在弹出的窗口中输入Name和ID。然后在Custom Attributes区域中,点击add,在弹出的窗口中输入Name和Type创建该work item类型的属性。

    添加新的WorkFlow:

    (1)在Team Artifacts视图中,右键Project Area,选择Open。选择Process Configuration,展开Project Configuration->Configuration Data->Work Items->Workflows。

    (2)点击Add,添加新的workflow.

    (3)在States区域中,添加该workflow所拥有的状态。在Actions区域中,添加用户可以采取的Action。

    (4)在Transition矩阵中,对于每一种状态的转移,在新老状态所对应的格子里选择合适的Action.

    建立WorkItem类型与WorkFlow的关联,完成状态转移模型的配置。

    (1)在Team Artifacts视图中,右键Project Area,选择Open。选择Process Configuration,展开Project Configuration->Configuration Data->Work Items->Workflow bindings。

    (2)选择WorkItem类型,选择WorkFlow,点击Save保存配置。

    此外,你可以通过编程方式得到WorkItem的状态,以及可采取的Action:

    IWorkItemClient client= (IWorkItemClient) repository.getClientLibrary(IWorkItemClient.class);
    IWorkflowInfo workflowInfo = workflowInfo = client.findWorkflowInfo(workItem, null);
    Identifier<IState> state= workItem.getState2();
    if (state != null) {
        Identifier<IWorkflowAction>[] actionIds= workflowInfo.getActionIds(state);
        if (actionIds.length > 0) {
            for (int i= 0; i < actionIds.length; i++) {
                Identifier<IWorkflowAction> actionId= actionIds[i];
                ChangeWorkItemStateAction action= new ChangeWorkItemStateAction(workflowInfo.getActionName(actionId), workItem, actionId,this);
                URL url= workflowInfo.getActionIconName(actionId);
                if (url != null) {
                    ImageDescriptor descriptor= WorkItemUI.getImageDescriptor(url);
                    if (descriptor != null)
                    action.setImageDescriptor(descriptor);
                }
            }
        }
    }

    在EasyInspect中,我们便采用了编程的方式来获取可进行的操作。

6.插件的部署

    如果对Jazz平台进行了扩展,则部署通常会包含客户端及服务器端两部分。EasyInspect除了在客户端增加了新的视图,而且创建了新的工作项的类型。对于新增加的客户端的视图,其部署相对较为简单,同部署Eclipse插件的步骤是一样的。服务器端的部署相对来说步骤会多一些,如下是EasyInspect采用的服务器端部署方式,:

    (1)首先要准备好你拥有的RTC的许可证文件

    (2)将服务器端需要部署的JAR包拷贝的Jazz应用程序的部署目录中的“jazz\WEB-INF\eclipse\plugins”中

    (3)编辑“\jazz\WEB-INF\eclipse\configuration\”目录中的config.ini文件,将部署的JAR包信息加入其中,以下示例是EasyInspect添入的信息:

    …
    com.ibm.team.repository.jndi@start, \
    org.apache.naming@start, \
    com.ibm.team.rtc.inspection.common@start, \
    org.opengroup.arm40-fragment
    osgi.bundles.defaultStartLevel=4
    …

    (4)清除Jazz应用程序的临时目录中的缓存信息,并重新启动Tomcat或WAS服务器

    (5)重新上传许可证

    完成这些步骤,你就可以体验你对Jazz平台的扩展成果了,并可以选择将你的成果与其他的Jazz用户来分享。

    以上是我们在使用和学习Jazz/RTC过程中的一些具体的实践经验,仅供大家参考,同时我们也欢迎大家一起就在Jazz平台进行扩展进行探讨。此外如果大家想进一步了解EasyInspect,也可以跟我们联系。

0
相关文章