技术开发 频道

基于Jazz技术构建企业级Web2.0应用(3)

《基于Jazz技术构建企业级Web2.0应用》系列第一部分:数据模型设计与持久化

《基于Jazz技术构建企业级Web2.0应用》系列第二部分:服务层设计与实现

【IT168 专稿】

    本系列的第一、二部分分别介绍了基于Jazz的数据存储模型和业务服务的实现。本文作为本系列的第三部分,将结合第一部分中介绍的“PetStore用户购买宠物”用例的实现,介绍如何利用Jazz提供的Process构件来完成业务流程的管理。Process作为一个Jazz核心构件,用于定制Project Area、Team Area相关的准则和流程,支持软件项目团队制定符合自身需要的、包含了非常好的实践的流程和规范。Process的一个作用是为其它Jazz构件提供支持,从而实现其中某些操作的AOP,本文将对此进行详细介绍。

Jazz AOP介绍

    Jazz Process机制提供了operation作为切入点的表示(注意:除此之外,operation还是Jazz进行权限控制的手段,此处不做详细介绍)。任何一个Jazz构件都可以通过operation来封装一定的业务逻辑,从而允许Process在这段业务逻辑的执行过程中施加某些影响。影响的方式包括两种:前置通知和后续行为。在一个operation执行之前执行的前置通知称为advisor,在一个operation执行之后执行的后续行为称为participant。一个operation可以对应多个advisor和participant。当一个operation要被执行时,Process机制将首先执行它对应的所有advisor,如通过则执行operation,如operation执行正确则执行其所有的participant。需要注意的是,Jazz operation分为客户端operation和服务器端operation两种,分别运行在Jazz服务器和客户端(Rational Team Concert)上。服务器端operation及其advisor、participant的执行是在一个事务中进行的,任何错误都将导致整个过程回滚(即便是最后一个participant抛出异常);而客户端operation及其advisor、participant的执行不是在一个事务中,因而如果某个participant执行出错,将只会停止后续participant的执行,而不会导致前面操作结果的取消。

PetStore AOP设计

    从AOP的角度观察,PetStore中“用户购买宠物”这一行为应当作为一个切入点被抽取出来,以支持围绕该行为前后的事件,如在用户购买成功之后向用户发送一封email,告知已付费金额。

    在本文中,我们在PetStore中增加一个服务,以实现“用户购买宠物”这一功能,并通过引入Jazz Process控制机制,实现对“用户购买宠物”这一行为的AOP,包括:

    1.增加一个RPC服务ICustomerService,其中的buyProduct方法负责向用户提供购买宠物的业务接口。

    2.定义和实现一个服务器端的operation "buy",用于封装实现“用户购买宠物”的业务逻辑,它将被ICustomerService的实现类调用以实现buyProduct方法。

    3.定义和实现一个针对operation"buy"的participant "buyParticipant",负责在用户购买宠物成功后向其发送一封email,告知已付费金额。

    用户购买宠物的执行流程如图1所示。


    图1. 购买宠物操作的时序图

    注意:上图中的IServerProcess是由Jazz Process提供,负责服务器端operation的执行,下面将在实现过程中对其进行进一步介绍。

PetStore AOP实现

    一个Jazz构件若需要Jazz Process的支持(或者说“向Process开放”),需要完成以下几个方面:

    · 通过configurationPoints扩展点定义operation。operation是Jazz Process进行AOP的切入点。

    · operation的实现。通常将业务逻辑(如对其它服务的调用)封装在一个com.ibm.team.process.common.advice.AdvisableOperation的run方法中,并通过Process构件提供的com.ibm.team.process.service.IServerProcess中的adviceAndExecute方法调用。注意:operation的定义与实现的绑定是在AdvisableOperation构造方法中,将参数operationId赋值为operation定义中的id。

    · 通过operationAdvisors和operationParticipants扩展点定义针对operation的前置通知(advisor)和后置行为(participant)。

    · advisor和participant的实现(分别实现com.ibm.team.process.common.advice.runtime. IOperationAdvisor和com.ibm.team.process.common.advice.runtime. IOperationParticipant接口)。

    · 通过process定义文件将operation和它的advisors、participants关联起来(分别通过operation的preconditions和follow-up actions字段)。

    下面将详细介绍如何在PetStore中利用Jazz Process机制实现上面设计的AOP功能。

    第一步,添加对Process bundle的依赖

    在插件项目com.ibm.petstore.service中添加对两个process bundle com.ibm.process.common和com.ibm.process.service的依赖。

    第二步,定义和实现operation "buy"及其participant "buyParticipant"

    在com.ibm.petstore.service的plugin.xml中通过configurationPoints扩展点定义operation "buy":

    清单1. operation定义

<extension
         point="com.ibm.team.process.service.configurationPoints">
      <operation
            id="com.ibm.petstore.service.buy"
            name="Buy Pet">
      </operation>
</extension>

    注意:这里较为重要的是id的定义。在下面的步骤中将通过对此id引用这里定义的operation。

    用一个继承自com.ibm.team.process.common.advice.AdvisableOperation的类BuyPetOperation实现operation "buy",在其run方法中封装用户付费的业务逻辑:

    清单2. BuyPetOperation

public class BuyPetOperation extends AdvisableOperation {
   
    private int accNo;
    private String pw;
    private int price;
    private String email;
   
    public BuyPetOperation(ITeamArea teamArea,
                   int accNo, String pw, int price, String email) {
        super("com.ibm.petstore.service.buy", null, teamArea);
        this.accNo=accNo;
        this.pw=pw;
        this.price=price;
        this.email=email;
    }

    @Override
    public IOperationReport run(IBehaviorConfiguration configuration,
            IProgressMonitor monitor) throws TeamRepositoryException {
        PayUtil payUtil=new PayUtil();
        try {
            payUtil.login(accNo, pw);
            payUtil.pay(price);
            payUtil.logout();
        } catch (PayUtilException e) {
            throw new TeamRepositoryException(e);
        }
        return null;
    }
   
    public String[] getActions() throws TeamRepositoryException {
        return new String[] { "buy" };
    }

    public String getEmail() {
        return email;
    }

    public int getPrice() {
        return price;
    }

}

    注意:在BuyPetOperation的构造方法中通过引用plugin.xml中operation定义的id值将实现类与operation的定义绑定,该id值将作为参数被传给AdvisableOperation的构造方法以完成operation实例的构造。

    在com.ibm.petstore.service的plugin.xml中通过operationParticipants扩展点定义针对operation "buy"的participant "buyParticipant":

    清单3. participant定义

<extension
         point="com.ibm.team.process.service.operationParticipants">
      <operationParticipant
            class="com.ibm.petstore.service.process.BuyPetParticipant"
            id="com.ibm.petstore.service.buyParticipant"
            name="Buy Pet Participant"
            operationId="com.ibm.petstore.service.buy">
      </operationParticipant>
</extension>

    这里发生了两件事:1)定义了此participant的id;2)通过operationId标识此participant所针对的operation。

    用一个实现IOperationParticipant接口的类BuyPetParticipant实现"buyParticipant",在其run方法中实现向用户发送email的功能:

    清单4. BuyPetParticipant

public class BuyPetParticipant implements IOperationParticipant {

    public void run(AdvisableOperation operation,
            IProcessConfigurationElement participantConfig,
            IParticipantInfoCollector collector, IProgressMonitor monitor)
            throws TeamRepositoryException {
        String email=((BuyPetOperation)operation).getEmail();
        int price=((BuyPetOperation)operation).getPrice();
        this.sendEmail(email,
              "From PetStore: Amount "+price+" is paid by your account.");
    }
   
    public void sendEmail(String email, String content){
        ……
        System.out.println("log: mail sent to \""+email+
                       "\" with content \""+content+"\".");
       
    }
}

    注意:同operation类似,一个participant也在其run方法中封装核心业务逻辑。在Jazz Process执行一个participant时,将把其对应的operation对象作为参数传递给run方法,本例中通过operation对象获取用户的email和购买宠物的价格信息。

    在PetStore的process定义文件中将operation "buy"和participant "buyParticipant"关联起来:

    清单5. operation和participant的关联

<process-specification xmlns="http://com.ibm.team.process">
<project-configuration>
         ……
</project-configuration>
    <team-configuration>
        <permissions>
            ……
        </permissions>
        <behavior>
            <role id="default">
            <operation id="com.ibm.petstore.service.buy">
                <preconditions>
                </preconditions>
                <followup-actions>
            <followup-action id="com.ibm.petstore.service.buyParticipant"
                        name="Buy Pet Participant"
                description="Buy Pet Participant contains sending a email to notify user pay amount.">
                    </followup-action>
                </followup-actions>
            </operation>
        </role>
        </behavior>
    </team-configuration>
</process-specification>

    注意:在process定义中通过id引用一个已定义的operation,并在follow-up actions字段中通过id引用它对应的所有participant。类似的,如果它有对应advisor,则需要在preconditions字段中指明。

    第三步,定义和实现ICustomerService

    在公共bundle com.ibm.petstore.common中定义服务接口com.ibm.petstore.common.service. ICustomerService:

    清单6. ICustomerService定义

public interface ICustomerService {
   
    public void buyProduct(IProduct product,int accountNo,String password,String email) throws TeamRepositoryException;

}

    在com.ibm.petstore.common的plugin.xml中添加ICustomerService的声明:

    清单7. ICustomerService声明

<service
           kind="RPC"
           name="ICustomerService"
           uri="com.ibm.petstore.common.service.ICustomerService"
           version="1">
</service>

    在服务bundle com.ibm.petstore.service中添加类com.ibm.petstore.service.CustomerService实现ICustomerService接口:

    清单8. CustomerService

public class CustomerService extends AbstractService implements ICustomerService {

    public void buyProduct(IProduct product, int accountNo, String password,
                       String email) throws TeamRepositoryException {
       
        IProcessServerService processServerService =
getService(IProcessServerService.class);
        ITeamArea teamArea = getTeamArea("PetStoreUserTeam");
       
        if(teamArea==null)
            throw new TeamRepositoryException(
                      "petstore user team doesn't exist");
       
        IServerProcess serverProcess = processServerService
                .getServerProcess(teamArea);
       
        BuyPetOperation buyOperation = new BuyPetOperation(teamArea, accountNo,
                password, product.getPrice(), email);

        serverProcess.adviseAndExecute(buyOperation);
    }
   
    ……
}

    注意:在buyProduct方法中通过IServerProcess的adviceAndExecute方法调用了operation"buy"。

    在com.ibm.petstore.service的plugin.xml中将ICustomerService及其实现类绑定:

    清单9. ICustomerService与实现类的绑定

<serviceProvider
            componentId="com.ibm.petstore"
            implementationClass="com.ibm.petstore.service.CustomerService">
         <provides>
            <providedService
                  interface="com.ibm.petstore.common.service.ICustomerService">
            </providedService>
         </provides>
         <prerequisites>
            <requiredService
                  interface="com.ibm.team.repository.common.service.IQueryService">
            </requiredService>
            <requiredService
                  interface="com.ibm.team.repository.service.IRepositoryItemService">
            </requiredService>
            <requiredService
                  interface="com.ibm.team.process.service.IProcessServerService">
            </requiredService>
         </prerequisites>
</serviceProvider>

    注意:这里对于IProcessServerService的依赖。

    第四步,为PetStore定义、部署process,并创建Project Area、Team Area

    启动Jazz服务器,在Rational Team Concert中创建一个到到本地存储库的连接。

    在连接中创建一个Process模板"petstore process definition":


    图2. 创建Process模板
 
    在打开的"petstore process definition"中选择"Process Configuration Source",配置如下:

    清单10. PetStore Process定义

<?xml version="1.0" encoding="UTF-8"?>
<process-specification xmlns="http://com.ibm.team.process">
<project-configuration>
        <initialization>
        </initialization>
        <permissions>
            <role id="default">
                <project-operation id="com.ibm.team.process.server.saveProjectArea">
                    <action id="createTeamArea"></action>
                </project-operation>
            </role>
        </permissions>
</project-configuration>
    <team-configuration>
        <permissions>
            <role id="default">
                <operation id="com.ibm.team.process.server.saveTeamArea">
                    <action id="teamArea">
                        <action id="any"/>
                    </action>
                </operation>
                <operation id="com.ibm.petstore.service.buy">
                    <action id="buy"/>
                </operation>
            </role>
        </permissions>
        <behavior>
            <role id="default">
                <operation id="com.ibm.petstore.service.buy">
                    <preconditions>
                    </preconditions>
                    <followup-actions>
                <followup-action id="com.ibm.petstore.service.buyParticipant"
                            name="Buy Pet Participant"
                        description="Buy Pet Participant contains sending a email to notify user pay amount.">
                        </followup-action>
                    </followup-actions>
                </operation>
            </role>
        </behavior>
    </team-configuration>
</process-specification>

    在连接中创建一个Project Area"PetStorePrj",并选择使用process模板"petstore process definition"。


    图3. 选择Process模板

    在"PetStorePrj"中创建一个Team Area"PetStoreUserTeam"。

    注意:由于process定义中已经将执行operation"buy"的权限赋予所有用户(role id="default"),这里无需对PetStoreUserTeam进行额外配置。

执行结果

    用户选择购买某个宠物后,系统将向其发送一封email,图4为log信息。


    图4. 执行结果
 
总结

    Process是Jazz的一个核心构件,它的一个重要作用是为其它构件提供业务流程管理支持。本文详细阐述了Jazz Process中 AOP的一些基本概念和使用方法,并结合PetStore应用介绍了如何在基于Jazz的开发中利用Process机制实现基于AOP的业务流程管理。

参考资源

    关于Jazz Project Area、Team Area及Process等基本概念,请参考Getting Started with Jazz Project Areas and Process

    关于Jazz Process的更多内容请参考Process开发指南

0
相关文章