实现BPEL4WS演示:教程
http://www.ibm.com/developerworks/cn/education/webservices/ws-bpelws/bpel_tutorial_cn.html
开始
什么是Business Process Execution Language for Web Services
Business Process Execution Language for Web Services(BPEL4WS)是IBM、BEA、Microsoft、SAP和Siebel共同开发的规范,定义了一种表示法来指定基于Web服务的业务流程行为。它将成为组合Web服务的标准。 因此,BPEL4WS的基本思想是将业务流程管理的能力与Web服务的灵活性和通用性相结合。
演示概述
本教程是开发者园地中的BPEL4WS演示的一部分。在演示中实现的市场案例演示了买方和卖方在协商期间彼此之间的交互。它提供了可中断的工作流,并且说明了相关性的使用。在演示中,您将能够依次模拟买卖双方的活动。首先,您模拟买方创建一份销售订单。接着,验证订单并计算营业税,在演示中,只要买方确认他或她同意总的订单价格和营业税,她或她就可以把销售订单提交到卖方系统中。下一步是评估买方的偿付能力。注意,订单验证、税金计算和偿付能力检查都是由第三方Web服务进行的。如果买方的偿付能力检查成功,卖方就复查订单,以决定是否批准或拒绝订单。这是本案例的最后步骤。此后,买卖双方就可以监控和删除订单。
BPEL4WS演示的重要特征是活动监视器――――在订单生命周期中的任何时间,买卖双方都可以看到订单状态更改的历史。这些状态与组成业务流程的活动相对应。
请参阅演示使用说明,以获得演示业务案例的详细描述。
试试这个生动的演示
可以在开发者服务器上找到BPEL4WS演示
注意:演示需要使用Internet Explorer 5.0(或更高版本)或Netscape Navigator 4.5(或更高版本)。
先决条件
为了创建和运行演示,您必须在Windows机器上安装以下必备软件:
- WebSphere Studio Application Developer 5.0(或Eclipse 2.1+)。
- WebSphere Application Server Advanced Edition 5.0。
您还应该下载以下软件:
- 市场演示代码(bpel4ws_demo.zip)。
- IBM BPWS4J Editor――――Eclipse插件,它提供了一个简单的编辑器,可以用来创建和修改BPEL4WS文件。
注意:您不需要下载BPWS4J引擎,因为它包括在演示代码之中。
基础
建立通用语言:业务流程和工作流
本教程的主要目的是演示您可以如何用Business Process Execution Language for Web Services(BPEL4WS)来创建健壮的实际工作流。在深入研究实现问题之前,让我们简要描述一下BPEL4WS的和新概念。As it is possible to infer from the 从BPEL4WS标题可以看出,本教程将讨论一种言语,它组合了两种技术:Business Process Management(业务流程管理)和Web服务。由于目前对这些技术的作用有许多夸大其词的宣传,所以用于描述什么是Web服务和业务流程管理以及如何实现Web服务和业务流程管理的语言越来越多,简直有些让人眼花缭乱。这就是为什么我们将首先讲解语义基础的原因,语义基础对于理解BPEL4WS和演示的体系结构是非常必要的。下面,我们定义本教程中所用的最重要的术语,并且简要描述将讨论的技术的背景。
让我们首先定义业务流程管理。
在当今激烈竞争的环境中,管理和改进自己的业务流程对于任何公司来说都是至关重要的。Total Quality Management(全面质量管理)的原则之一就是:"产品的质量是由开发和维护它的流程的质量决定的"。因此,业务流程无疑是改进任何组织的关键作用点。ISO 9001、CMMI和其他用于质量管理和组织改进的流行模型和标准关注的重点就是流程。有多种定义流程的方法,而每种方法都不尽相同。在本教程中,我们将采用Workflow Management Coalition(WFMC,http://www.wfmc.org)的定义,WFMC是一个非赢利的国际性组织,其会员包括工作流厂商、用户、分析员和大学/研究团体。
因而,根据WFMC定义:
业务流程是一组相连的过程或活动,其中包含的过程或活动可以是一个,也可以是多个,通常在定义功能角色和相互关系的组织结构方面共同实现业务目标或策略意图。
工作流是业务流程的自动化(整体或部分),其中,文档、信息或任务可以遵循一组程序上的规则从一个参与者传送到另一个参与者。
从上面的定义中,我们能够理解这两个术语之间的相互关系。术语业务流程通常用在商业环境中, 而工作流是一个IT术语,它表示业务流程的自动化。另外,单词流程也常常与这两个术语一起使用。在正式的讨论中,根据语境的不同,它可能意味着业务流程,也可能意味着工作流。在下文中,我们还将介绍一些新概念的定义。业务流程的例子可以是为办公室安排物品的次序,也可以是管理仓库中的库存。在组织中建立一致的流程可以剔除工作中随意的成分,从而使工作更有效率,更具有可预测性。正如WFMC白皮书中所强调的,业务流程的自动化(创建工作流)可以带来很多好处:它通过减少不必要的步骤来提高效率,通过标准化工作方法实现了更好的流程控制,并且因为对流程的软件控制能够使重新设计与业务需求的改变保持同步,所以提供了更多的灵活性。理解、编写(codifying)、自动化和改进公司开展业务的方式的艺术常常称为业务流程管理(Business Process Management,BPM)。BPM把业务流程看作是开展任何业务活动的关键所在。
上面给出的定义是一般性的。例如,它们并没有指定工作流组件(component)之间的接口是什么以及工作流的构件(building block)是什么。同时,所有的工作流组件显然应该是松散耦合的,并且尽可能灵活地提供最大程度的互操作性。这样,Web服务就可以发挥作用。
建立通用的语言:Web服务
根据W3C的定义(请参见http://www.w3.org/TR/2002/WD-ws-gloss-20021114/):
Web服务是由URI标识的软件系统,其公共接口和绑定可以使用XML进行定义和描述。其他的软件系统能够发现Web服务。然后,这些系统就可以通过Internet协议传送的基于XML的消息按照Web服务定义规定的方式与其进行交互。
正如BPEL4WS规范中所强调的,Web服务的目标是使用Web标准实现应用之间的普遍互操作性。Web服务使用松散耦合的集成模型,可以在不同的领域(包括企业对消费者、企业对企业和企业应用)内提供灵活的异构系统集成。Web是创建开放的分布式系统的构件(building block),允许公司和个人快速而便宜地使其数字资源可以为全世界所用。
同时,没有Web服务是孤立的。在涉及两方或多方的长期交互中,业务交互的实际模型一般假定消息交换的顺序为同步或异步,这样,我们就自然而然想到通过工作流的方式把Web服务组合成不同凡响的样式。这种观念是BPEL4WS的基础。
融入一体:BPEL4WS
BPEL4WS定义了描述业务流程的XML语法。BPEL4WS流程模型的关键之处在于Web服务之间的peer-to-peer交互的表示法,Web服务是用WSDL进行描述的,而WSDL是Web服务描述的事实标准。流程本身及其合作伙伴都模型化为WSDL服务。我们使用BPEL4WS语法来创建流程定义。根据WFMC定义,流程定义是以支持自动化操作(如通过工作流管理系统进行建模或加以实现)的形式表示业务流程的方法。流程定义确定了如何协调流程实例及其合作伙伴之间的交互。
现在,让我们讨论市场演示示例中的BPEL4WS语法和概念。
描述流程
准备开始演示
在创建市场流程之前,请确定您是否有演示主页上指定的所有必备的软件。
注意,为了运行我们基于工作流的演示,我们需要工作流引擎,也就是为流程实例提供运行时环境的软件服务。在我们的演示中,我们把IBM Business Process Execution Language for Web Services Java Run Time(BPWS4J)作为工作流引擎。事实上,BPWS4J包括两个基本组件:
- 可以执行的平台,在这个平台上,可以用BPEL4WS编写业务流程业务流程(它包括在演示代码中,请参见演示主页)。
- Eclipse插件,提供了一个简单的编辑器,可以用来创建和修改BPEL4WS文件(它应该可以单独下载,请参见演示主页)。
在下载了全部所需的软件之后,您就应该把Eclipse插件安装到WebSphere Studio Application Developer(Application Developer)或另一个环境中,和BPWS4J文档中指定的一样。您还应该把带有演示代码的bpel4ws_demo.zip档案文件(archive file)解压缩到本地驱动器中。
首先,我们将讨论市场演示流程WSDL描述。然后,我们将创建可部署的BPEL4WS流程定义。再将BPWS4J引擎本身作为Web应用安装到WebSphere Application Server中。最后,我们将把流程部署到BPWS4J中,并且运行演示。在我们开始之前,您可能需要阅读演示使用说明,以了解演示领域和业务场景。注意,描述如何创建合作伙伴Web服务(实现为Java类)、WSDL文件和前端JSP不在本教程的范围之内。不过,所有这些都作为演示代码提供了。记住,它们都可以用Application Developer进行创建(请参见参考资料以获得更多信息)。
市场演示流程WSDL描述
如上所述,BPEL4WS流程本身及其组件都是Web服务。BPEL4WS依赖调用的服务的WSDL描述来引用正在交换的消息、正在调用的操作和这些操作所属的portType(端口类型)。之所以要创建基于BPEL4WS的工作流,是因为我们需要定义流程和组件的WSDL描述。 我们也需要创建定义整个流程的BPEL4WS文件。所有这些文件都是BPWS4J部署流程所需要的。
市场流程的WSDL描述包含在DemoProcess.wsdl和Definitions.wsdl文件中。这些文件都在<demo_dir>/definitions
目录下。这里,<demo_dir>
是将演示档案文件(archive file)解压缩到的目录。它还包含合作伙伴的Web服务的WSDL描述。下面,我们将讨论流程的WSDL描述最重要的代码片断。让我们首先看一看端口类型的代码(清单1:市场流程WSDL代码片断(DemoProcess.wsdl)),它组织了我们的流程将展示的操作。
清单1:市场流程WSDL代码片断(DemoProcess.wsdl)
<portType name="BuyerPT">
<operation name="create">
<input message="def:Order"/>
<output message="def:Status"/>
</operation>
<operation name="agree">
<input message="def:ID"/>
<output message="def:Status"/>
</operation>
</portType> <portType name="SellerPT">
<operation name="approve">
<input message="def:ID"/>
</operation>
</portType> <portType name="CommonPT">
<operation name="find">
<input message="def:ID"/>
<output message="def:Order"/>
</operation>
<operation name="terminate">
<input message="def:ID"/>
</operation>
</portType>
由于演示中的参与者将具有两个角色(买方和卖方),所以我们需要定义它们各自可以使用哪些操作。从清单1:市场流程WSDL代码片断(DemoProcess.wsdl)中可以看出,买方可以创建新的订单以及同意(或不同意)为订单项目(包括营业税)付款。卖方批准由买方创建的订单。注意,CommonPT端口类型指定可以由买卖双方调用的操作。他们都可以用指定的ID终止流程实例或搜索流程实例。记住,可以将Definitions.wsdl文件中指定的三种数据结构用作操作参数。Order订单代表销售订单,并且包括订单参数,比如订单ID、创建日期、订购线(order line)、买方信息、订单状态更改历史等等。Status状态结构包含关于当前订单状态的信息。ID结构包含订单ID和状态。
以下WSDL代码片断(清单2:市场流程WSDL代码片断(DemoProcess.wsdl))展示了BPEL4WS规范中提议的非标准WSDL扩展。
清单2:市场流程WSDL代码片断(DemoProcess.wsdl)
<bpws:property name="pid" type="xsd:string"/>
<bpws:propertyAlias propertyName="tns:pid" messageType="def:ID" part="id"/>
<bpws:propertyAlias propertyName="tns:pid" messageType="def:Order"
part="order" query="//id"/> <slt:serviceLinkType name="SellerSLT">
<slt:role name="salesSystem">
<portType name="SellerPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="BuyerSLT">
<slt:role name="buyingSystem">
<portType name="BuyerPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="AnyoneSLT">
<slt:role name="anyone">
<portType name="CommonPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="ValidatorSLT">
<slt:role name="validator">
<portType name="svc:ValidationUtilityPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="CalculatorSLT">
<slt:role name="calculator">
<portType name="svc:CalculationUtilityPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="AssessorSLT">
<slt:role name="assessor">
<portType name="svc:SolvencyUtilityPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="SynchronizerSLT">
<slt:role name="synchronizer">
<portType name="svc:SynchronizationUtilityPT"/>
</slt:role>
</slt:serviceLinkType>
<slt:serviceLinkType name="MeterSLT">
<slt:role name="meter">
<portType name="svc:MeteringPT"/>
</slt:role>
</slt:serviceLinkType>
上面的代码片断首先展示的是bpws:property
定义。特性定义创建了全局惟一的名称,并且使其与XML Schema类型相关联。第一个bpws:propertyAlias
元素定义了全局命名的特性tns:pid
,以作为消息类型def:ID
的id
部分的别名。第二个bpws:propertyAlias
元素说明了如何用query
属性来标识id部分中的单一值。根据BPEL4WS规范,查询语言可以由<process>
元素的queryLanguage
属性进行指定。XPath 1.0就是由此属性的缺省值进行表示的。注意,我们在这里看到了BPEL4WS属性的典型用法――――命名一个标记来使服务实例与消息相关联。在本教程后面,我们将讨论关联的概念。
serviceLinkType
通过定义关系中每种服务所扮演的角色和每个角色所提供的portTypes(端口类型)来链接两个交互的服务。在这些服务中,有一个就是我们的流程。它可以调用其他的服务或者给其他的服务提供自己的操作。例如,SellerSLT serviceLinkType(服务链接类型)将用于把卖方链接到流程中。充当卖方的合作伙伴将能够从SellerPT端口类型中调用操作。在这个交互中,我们的流程作为销售系统。同样,BuyerSLT serviceLinkType(服务链接类型)将用于把买方链接到我们的流程中。因此,服务类型链接的作用是声明流程如何根据各方提供的操作类型与另一个服务进行交互。注意,我们需要Note that we need the 验证者(validator)、计算者(calculator)和评估者(assessor)角色,来表示我们的流程将为订单验证、税金计算和买方偿付能力检查调用的第三方服务。我们还声明了两个辅助角色:测量计(meter)和同步器(synchronizer)。我们使用测量Web服务(Metering Web服务)来计算流程实例的数目。此Web服务与流程之间的交互最吸引人的地方是流程中交互的主动性。它在流程实例的数目改变时通知测量Web服务服务。这是非常重要的,因为我们应该控制现有实例的数量以使其与可用的系统资源协调。下面,我们将描述为了不耗尽内存我们应该如何控制流程实例的数量。在我们的演示中,一旦买方或卖方调用了流程,流程与其合作伙伴之间的交互就会发生。只有在测量Web服务的情况下,我们才有由流程引发的通知模型。在我们的演示中,我们使用同步器来维护服务器端变量的一致性。此外,同步器可以看作是一种模式,其目的是适应我们需要对变量内容进行复杂转换的情况。可以由BPEL4WS技术(使用下面所描述的<assign>
活动和XPath表达式)应用于变量的转换是有限的。要进行同步Web服务,我们可以使用Java代码(或任何可用于实现我们的Web服务的语言)来转换变量。
在Application Developer中创建流程定义
引用流程定义
现在,我们将用Application Developer创建市场流程的BPEL4WS流程定义。为了方便起见,我们把完整的流程定义放在演示代码中,这样您就可以您创建的流程定义与引用的流程定义进行比较。引用的流程定义在<demo_dir>/definitions
目录下的DemoDefaultProcess.bpel文件中。我们假定您已经安装了前面描述的BPWS4J Eclipse插件。
请遵循以下步骤来完成此场景:
1. 创建新项目
在您的工作区创建新的项目,这个项目将包含在您的流程定义中。例如,它可以是名为BPEL4WS Demo的Java项目。
2. 打开BPWS透视图
在菜单中选择Window -> Open Perspective -> BPWS。
3. 创建BPEL4WS流程定义文件
在菜单中,单击File -> New -> Other。在左边的窗格中,选择BPWS,然后再由变得窗格中,选择BPWS File。
单击Next按钮。为您的流程定义选择合适的项目和文件名。
单击Next按钮。New BPWS File向导的最后一个窗口出现,它将展示顶层流程属性的列表。保留缺省值不变,然后单击Finish按钮。
这样,带有BPEL4WS流程定义的新文件就创建好了,并且会在BPWS4J编辑器中打开。BPWS4J编辑器是一个多页工具 ,它包含以下基本视图:Editor、Outline、Navigator、Tasks和Properties。要获得更详细的关于视图的描述,请参见BPWS4J帮助(可以Help -> Help Contents -> BPWS Guide获得)。
4. 设置基本的流程属性
在编辑器中选择Business Process:sampleprocess1。现在,我们可以更改流程属性(或者通过Properties视图,或者通过编辑器上的Source选项卡)。在我们的教程中,我们将使用两种方法。
在Properties视图中,将Name属性更改为marketplace。选择编辑器中的Source选项卡。这里,您可以看到流程定义的源XML表示。根据BPEL4WS规范,定义的根元素是 <process>
。流程中的所有活动也定义为XML元素。将targetNamespace
和xmlns:tns
属性设置为http://bpeldemo.ibm.com/processing/
。按CTRL+S键以保存您的更改。现在,我们的流程定义源代码应该入下所示:
清单3: 市场流程定义代码片断(DemoProcess.bpel)
<process xmlns="http://schemas.xmlsoap.org/ws/2002/07/business-process/"
name="marketplace"
targetNamespace="http://bpeldemo.ibm.com/processing/"
xmlns:tns="http://bpeldemo.ibm.com/processing/"
suppressJoinFailure="no"
variableAccessSerializable="no"
enableInstanceCompensation="no"
abstractProcess="no">
</process>
5. 创建合作伙伴
现在,我们将声明流程的合作伙伴。根据BPEL4WS规范,与业务流程交互的服务可以模型化为合作伙伴。每个合作伙伴的特征都由serviceLinkType进行描述。每个合作伙伴都有名称,并且这个名称可以用于所有与该合作伙伴进行交互的服务。这是非常重要的,例如,在将响应与同时请求相同类型的服务的不同合作伙伴进行关联时,就需要知道各个合作伙伴的名称。让我们讨论一下市场场景中需要什么样的合作伙伴。显而易见,我们需要买方 and 和卖方Seller合作伙伴,他们将调用我们在前面介绍服务链接类型时讨论的市场业务流程的方法。在此交互中,我们的流程将分别用作销售系统或购买系统。对于卖方或买方都可以调用流程的情况,我们还需要另一个合作伙伴。Validation(验证)、Calculation(计算)和Assessment(评估)第三方Web服务将分别扮演验证者、计算者和评估者的角色。Metering(测量)和Synchronization(同步)工具服务将扮演测量仪和同步器的角色。
要在PWS4J中创建新的合作伙伴,可以返回到编辑器的Process选项卡,再在Outline视图中选择marketplace流程,然后单击add partner图标。在Outline视图中,单击Unnamed合作伙伴,然后在Properties视图中指定合作伙伴的特性。将Name设置为 Seller,将My Role设置为salesSystem并且将Service link type设置为{urn:demo:MarketplaceService}SellerSLT。记住,BPWS4J使用花括号{}来将命名空间和本地名称分开。
以下是我们的演示流程中完整的合作伙伴列表。
清单4:市场流程定义代码片断(DemoProcess.bpel)
<partners>
<partner name="Seller" xmlns:ns1="urn:demo:MarketplaceService"
serviceLinkType="ns1:SellerSLT" myRole="salesSystem"/>
<partner name="Buyer" xmlns:ns2="urn:demo:MarketplaceService"
serviceLinkType="ns2:BuyerSLT" myRole="buyerSystem"/>
<partner name="Anyone" xmlns:ns3="urn:demo:MarketplaceService"
serviceLinkType="ns3:AnyoneSLT" myRole="anyone"/>
<partner name="ValidationService" xmlns:ns4="urn:demo:MarketplaceService"
serviceLinkType="ns4:ValidatorSLT" partnerRole="validator"/>
<partner name="CalculationService" xmlns:ns5="urn:demo:MarketplaceService"
serviceLinkType="ns5:CalculatorSLT" partnerRole="calculator"/>
<partner name="AssessmentService" xmlns:ns6="urn:demo:MarketplaceService"
serviceLinkType="ns6:AssessorSLT" partnerRole="assessor"/>
<partner name="SynchronizationService" xmlns:ns7="urn:demo:MarketplaceService"
serviceLinkType="ns7:SynchronizerSLT" partnerRole="synchronizer"/>
<partner name="MeteringService" xmlns:ns8="urn:demo:MarketplaceService"
serviceLinkType="ns8:MeterSLT" partnerRole="meter"/>
</partners>
您可以通过编辑器的可视界面来添加合作伙伴(和前面的示例类似),也可以在关闭</partners>
选项卡之前,从DemoDefaultProcess.bpel中手工复制和粘贴它们到BPEL4WS Source编辑器中(按CTRL+S键来保存源代码)。您将定义的流程合作伙伴集应该如下所示:
6. 创建变量和关联集
在为流程定义变量和关联集之前,让我们讨论流程实例process instance是什么。在前面我们非正式使用过这个术语,意思是流程的一种实现的表示。在我们的市场场景中,当买方调用流程来创建新的订单时,就创建了新的实例。另一个买方将引发另一个流程实例的创建,依此类推。每个实例都由当前正在进行的流程活动(例如,等待卖方的批准,或者在买方或卖方删除或监控项目时保持等待)和该实例从合作伙伴中接收的消息进行描述。BPEL4WS引入了存储正在交换的消息和其他流程数据的变量的概念。根据BPEL4WS规范,变量提供了一些方式,可以用于存放构成流程的状态的消息。它们既可以存放已经从合作伙伴接收的消息(或者将要发送到合作伙伴的消息),也可以存放作为“临时变量”用于计算的消息(这类消息从不与合作伙伴进行交换)。出于演示的目的,我们需要三个变量。第一个变量包含正在处理的订单。第二个变量和第三个变量将分别包含订单的状态及其ID。事实上,我们可以一个订单只使用一个来存储状态和ID两者。然而,如果我们这样做,那么在每次与合作伙伴进行交互时,就都需要传送订单的XML结构。此外,它将需要合作伙伴依赖于这个非标准的订单结构,这是不能接受的,因为合作伙伴可能是由第三方服务实现的。
如上所述,一个流程实例可以“等待”卖方的批准,而与此同时,另一个流程实例可能需要买方或卖方来监控流程。因此,发送到流程的消息不仅需要发送到正确的目的端口,而且还需要发送到正确的流程实例。在In BPEL4WS中,我们使用关联集的概念来完成此任务。关联集定义为由所有消息共享并且在流程实例的范围内实例化的特性集。类似于流程的实例化,关联集的实例化也是由专门标记的操作触发的。
要在BPWS4J中创建新的变量,可以返回到编辑器的Process选项卡,在Outline视图中选择marketplace流程,然后单击add variable图标。在Outline视图中单击Unnamed变量,然后在Properties视图中指定变量特性。将Name设置为order,并且将Message type设置为{http://bpeldemo.ibm.com/definitions/}Order。
以下是演示的变量的完整列表。
清单5:市场流程定义代码片断(DemoProcess.bpel)
<variables>
<variable name="order" xmlns:ns9="http://bpeldemo.ibm.com/definitions/"
messageType="ns9:Order"/>
<variable name="status" xmlns:ns10="http://bpeldemo.ibm.com/definitions/"
messageType="ns10:Status"/>
<variable name="id" xmlns:ns11="http://bpeldemo.ibm.com/definitions/"
messageType="ns11:ID"/>
<variable name="meteringEvent" xmlns:ns12="http://bpeldemo.ibm.com/definitions/"
messageType="ns12:meteringMessage"/>
</variables>
您可以通过编辑器的可视界面来添加变量(和前面的示例类似),也可以在关闭</variables>
选项卡之前,从DemoDefaultProcess.bpel 中手工复制和粘贴它们到BPEL4WS Source编辑器中(按CTRL+S键来保存源代码)。
要在BPWS4J创建关联集,可以返回到编辑器的,Process选项卡,在Outline视图中选择marketplace流程,然后单击add correlation set图标。 在Outline视图中选择Unnamed correlation set,然后在Properties视图中指定关联集特性。将Name设置为identifier。
将关联特性添加到关联集中。在Outline视图中选择identifier,然后单击add correlation property图标。在Outline视图中选择Unnamed property,然后在Properties视图中指定关联特性的属性。将Name设置为{urn:demo:MarketplaceService}pid。
7. 创建流程高层结构
在我们的流程中有四个主要部分。我们已经讨论了合作伙伴、变量和关联集。流程定义的剩余部分包含订单处理执行的逻辑的描述。任何BPEL4WS流程包括一组相关的基本活动,例如,它们可以接收合作伙伴的请求,调用Web服务,转换变量中的数据,将应答发送到合作伙伴,终止流程,等等。这些活动可以依次进行,也可以并行进行。结构化活动表示基本活动的变量。复杂的现实业务流程包含许多不同类型的活动。在我们的教程中,我们将把这些活动分解成块(block),并且依次讨论这些块。 首先,我们将讨论高层流程结构,然后一步一步地定义此结构的元素。这种方法常常称为自顶向下方法,相反,自底向上方法假定您已经定义了一组基本活动,然后把它们组织成主要块。
市场演示演示了可中断的工作流,也就是业务逻辑的长期、有状态和可中断表示。这假定有停止和等待合作伙伴介入的流程点。回顾一下演示使用说明,并且分析流程WSDL(请参见清单1:市场流程WSDL代码片断(DemoProcess.wsdl)),我们可以得出这样的结论,在五种情况下,外部合作伙伴可以应用到我们的流程中:
- 买方想要创建新订单(这应该循环进行,直到订单验证成功)
- 买方想要同意或不同意刚创建的订单的总数
- 卖方想要复查创建的订单,以批准或拒绝它
- 卖方或买方想要通过订单的ID来查找订单,以查看订单状态
- 卖方或买方想要删除订单(它同样要终止流程实例)
注意,这些活动是相关联的:某些活动可以同时进行,而其他的活动可以按照一定的次序进行。如下图所示:
图1:市场流程高层结构
根据这种非正式的流程描述,我们可以得出这样的结论,在我们的流程中,应该有三个主要的复杂流程是并行执行的:
- CREATION SEQUENCE
- SEARCH CYCLE
- TERMINATION SEQUENCE
为了对一组并行任务进行建模,BPEL4WS使用 uses the <流(flow)>
活动。只要启动流,流内的基本活动就并行启动。流活动也使得课以表达直接嵌套的或使用<链接>间接包含的活动之间的同步依赖性。因为我们的演示的三个主要块之间没有依赖性(它们应该并行进行而不考虑其他块的状态),所以我们不需要使用链接。
为了在BPWS4J中创建流,可以选择编辑器的Process选项卡,在编辑器中选择Business Process: marketplace,然后单击add flow图标。在编辑器中选择Flow:Unnamed,然后在Properties视图中指定流特性。将Name设置为Main_Flow,保留其他字段中的缺省值不变。
显然,流内所有这些复杂的活动应该按照它们的次序包含许多以一定的次序运行的子任务。现在,让我们更详细地讨论上述复杂活动。
8. 定义TERMINATION SEQUENCE(终止顺序)
市场演示场景从订单创建顺序开始。不过,我们把对这个块的讨论放在本教程的后面。首先,我们将讨论简单的结构化活动,然后逐步深入,最后理解更复杂的CREATION SEQUENCE(创建顺序)的逻辑。所以,在探讨更高级的主题之前,让我们首先了解最简单的活动,也就是TERMINATION SEQUENCE(终止顺序)。
如上所述,TERMINATION SEQUENCE活动应该包含许多依次执行的终止业务流程的活动。可以在BPEL4WS中通过<顺序>
活动的方式描述顺序任务的模式。这些活动按照它们在<顺序>
元素中指定的次序执行。我们的简单终止活动将只包含两个任务。它将通过<接收>
活动的方式获取合作伙伴的请求,而通过<terminate>
的方式终止当前的流程实例。
记住,我们使用<接收>
活动来给流程的合作伙伴提供服务。The <接收>
活动既应该有属性指定它希望从其接收消息的合作伙伴,也应该有属性指定它希望合作伙伴调用的端口类型和操作。应该强调的是,It should be stressed that <接收>
活动也可用于初始化流程。为了达到这个目的,我们需要注释<接收>
活动,并且把createInstance属性设置为"yes"。
要在BPWS4J中创建终止顺序,可以选择编辑器中的Process选项卡,展开expand Business Process: marketplace,并且选择Flow:Main_Flow。单击add sequence图标。在编辑器中选择Sequence:Unnamed,然后在Properties视图中指定顺序特性。将Name设置为TERMINATION_SEQUENCE,并且保留其他字段中的缺省值不变。
现在,让我们创建顺序中的<接收receive>
活动。选择Sequence:TERMINATION_SEQUENCE,然后单击add receive图标。在编辑器中选择receive:Unnamed,并且在Properties视图中指定特性。将Name设置为Terminate_Receive,Port type设置为{urn:demo:MarketplaceService}CommonPT,operation设置为terminate。为Partner选择Anyone,为Variable选择id,为Create instance选择false。这样,我们就创建了<接收>
活动,它通过任何合作伙伴都可以使用的CommonPT端口类型等待终止操作的服务清求。接收的消息将放在id变量中。
记住,在这种情况下,我们不需要创建新的流程实例。如前所述,应该通过使用关联集将其创建和选择好了。要将关联添加到<接收>
活动中,可以在编辑器中选择Receive:Terminate_Receive,然后单击Outline视图中的add correlation图标。在轮廓视图中,选择Correlation,然后在Properties视图中指定其特性。为Set选择identifier,为Initiate选择true。 注意,receive
是块操作。只有在流程实例接收到匹配的消息之后,它的执行才完成(并且顺序中的下一个活动将开始)。
要完成TERMINATION_SEQUENCE,我们需要添加<terminate>
活动,这会立即放弃执行该活动的流程实例中的所有执行过程。选择编辑器的Process选项卡,展开Business Process: marketplace,然后选择Sequence:TERMINATION_SEQUENCE。单击add terminate图标。在编辑器中选择Terminate:Unnamed,然后在Properties视图中指定顺序特性。将Name设置为Terminate,而保留其他字段中的缺省值不变。
9. 定义SEARCH CYCLE(搜索循环)
现在,让我们定义更复杂的SEARCH CYCLE活动,它包含我们等待合作伙伴定单清求的无限循环。BPEL4WS通过使用<while>
元素支持无限循环,它允许以类似于编程语言循环的方式重复执行指定的反复活动。要在BPWS4J中创建while循环,可以选择编辑器的Process选项卡,展开Business Process: marketplace,然后选择Flow:Main_Flow。单击add while图标。在编辑器中选择While:Unnamed,并且在Properties视图中指定顺序特性。将Name设置为SEARCH_CYCLE,Condition设置为0=0(因为它应该是一个无限循环),而保留其他字段中的缺省值不变。
BPEL4WS循环中应该只有一个活动。这里,我们将使用<顺序>
活动,因为我们需要顺序执行循环内的任务。所以我们将定义顺序命名的Search_Sequence,其中将有两个活动: <pick>
和<reply>
,如下所示:
清单6:市场流程定义代码片断(DemoProcess.bpel)
<sequence name="Search_Sequence" xmlns:demo="urn:demo:MarketplaceService"
xmlns:svc="http://bpeldemo.ibm.com/services/">
<pick name="Search_Pick" createInstance="no">
<onMessage partner="Anyone" portType="demo:CommonPT" operation="find"
variable="id" createInstance="no">
<correlations>
<correlation set="identifier" initiate="yes"/>
</correlations>
<empty name="Empty_Activity"/>
</onMessage> <onAlarm for="'P0Y0MT60M'">
<sequence name="NotifyAndTerminate_Sequence"> <invoke name="Notify_Invoke" partner="MeteringService"
portType="svc:MeteringPT" operation="decrease"
inputVariable="meteringEvent" outputVariable="meteringEvent"/> <terminate name="Terminate_Activity"/> </sequence>
</onAlarm>
</pick> <reply name="Search_Reply" partner="Anyone" portType="demo:CommonPT"
operation="find" variable="order">
<correlations>
<correlation set="identifier" initiate="yes"/>
</correlations>
</reply>
</sequence>
循环的第一个活动是<挑选>
。根据规范,这个活动等待一组事件中的某一个的出现,然后执行与出现的事件有关的活动。因此,<挑选>
的形式是形式事件/活动的一些分支,而只有一个分支将被选中,选择的根据是与该分支相关的事件在任何其他事件出现之前出现。可能的事件是某些消息、请求/响应操作或基于计时器的“警告”的到达。我们的<挑选>
的onMessage处理器等同于相应的<pick>
<接收>
活动,并且处理查找操作的输入请求。onAlarm处理器使用for属性来定义持续时间,之后,将生成警告事件,并且将执行包含在onAlarm元素中的活动。记住,只要没有onMessage区段处理的时长60分钟的请求消息,这就将发生。因此,在我们的场景中,<挑选>
的逻辑如下:只要创建了流程实例(当买方创建了新的定单时,这将发生),BPWS4J调度程序就开始60分钟时间间隔的等待。如果在此时间时间间隔内,满足onMessage处理器标准的的消息到达,我们将不做任何事情(即执行<空>
活动),然后把请求订单发送到合作伙伴(使用<应答>
),并且开始下一个60分钟时间间隔的等待(和我们有一个循环一样)。然而,如果在60分钟时间间隔内没有请求到达(买方和卖方都不想监控订单状态),我们就假定将不再使用该订单,并且可以将其删除。我们通过执行<终止>
活动来终止流程实例,和我们前面讨论的一样。当然,此逻辑与实际销售系统相差甚远。我们之所以使用60分钟的值是因为演示的特殊性。记住,我们使用XML Schema类型duration来定义时间间隔,所以'P0Y0MT60M'的时间间隔为0年0月60分。
在终止onAlarm元素中的流程实例之前,我们调用测量Web服务来通知它流程终止。它将把客户端上流程实例的数量减1。下面是我们如何使用<调用>
活动来调用合作伙伴提供的Web服务的例子。除了操作和端口类型之外,同步流程还指定了输入变量和输出变量。可以用一个或多个关联集来关联流程实例与合作伙伴端有状态的服务。
Search_Sequence的第二个活动是<应答>
。如上所述,它可以用来把响应发送到先前通过<挑选>
方式接受的请求中。我们还可以应答通过<接收>
接受的请求。类似于<调用>
和<接收>
活动,<应答>
活动也是通过合作伙伴、操作、端口类型、输入变量和关联进行描述的(只要我们处理的是可中断的工作流)。
您可以从清单6中添加定义,要达到这个目的,有两种方式,第一种是通过BPWS4J可视界面(和前面的示例类似),第二种是在关闭SEARCH_CYCLE的</while>
标签之前,手工从DemoDefaultProcess.bpel中复制它们,然后粘贴到 and paste to the BPEL4WS Source编辑器中。记住,如果您使用BPWS4J Property编辑器,就应该遵循其定义XML限定名的格式。例如,在我们的清单中,流程定义的onMessage元素的portType属性被设置为demo:CommonPT。这里,demo是命名空间urn:demo:MarketplaceService的简写。然而,我们应该将Port type属性定义为{urn:demo:MarketplaceService}CommonPT。在我们的演示中,我们主要使用命名空间来引用所需的WSDL信息,在WSDL信息中声明了消息定义和其他元素。
10. 定义CREATION SEQUENCE(创建顺序)
如上所述,CREATION SEQUENCE活动应该包含许多依次执行的任务,它由BPEL4WS中的 <sequence>
活动进行建模。将顺序命名的CREATION_SEQUENCE添加到主流中,和我们前面说明的一样。
现在,让我们看一看顺序内的基本活动。我们需要while循环来创建新的次序,并且对其进行更新,直到它有效为止。<while>
活动类似于编程语言中的while循环。和编程语言中通常所做的一样,在循环之前,我们需要初始化变量(它用作循环执行条件)。在BPEL4WS中,<分配>
活动既可以用来把数据从一个变量复制到另一个变量中,又可以用来通过表达式构造和插入数据。清单7展示了在初始化循环之前,我们如何使用分配。
清单7:市场流程定义代码片断(DemoProcess.bpel)
<assign name="Status_Assign">
<copy>
<from>
<status xmlns="" xsi:type="def:Status"
xmlns:def="http://bpeldemo.ibm.com/definitions/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<time xsi:type="xsd:long">0</time>
<type xsi:type="xsd:short">-2</type>
<role xsi:type="xsd:string">Buyer</role>
</status>
</from>
<to variable="status" part="status"/>
</copy>
</assign>
从DemoDefaultProcess.bpel中复制<分配>
活动,然后使用BPWS4J Source编辑起来将分配插入刚刚创建的CREATION_ SEQUENCE内的流程定义中。
现在,我们就可以使用循环本身了。它只包含一个我们还没有在前面讨论的元素:<开关>
。这个活动的观念也是来自编程语言。它支持有条件的行为,是由一列有序的条件分支组成的。条件分支可以有一个,也可以有多个,它们是由case元素进行定义的。条件分支后面可以有otherwise分支。
记住,在我们的流程定义的<分支>
和<while>
活动中,我们将订单状态作为过渡条件。下面是某些最重要的订单状态的清单,它可以用在流程描述中:
表1:订单状态
Status Code | -3 | -2 | -1 | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|---|---|---|
Status | Canceled | Updated | Error | Created | Validated | Calculated | Agreed | Checked |
我们已经讨论了所有的CREATION WHILE元素。 DemoDefaultProcess.bpel文件包含CREATION WHILE的完整内容。您可以通过两种方式添加其定义,第一种方式是通过BPWS4J可视界面(和前面的示例类似),第二种方式是在关闭CREATION_ SEQUENCE的</sequence>
标签之前,手工从DemoDefaultProcess.bpel中复制它,并粘贴到BPEL4WS Source编辑器中。记住,如果您使用BPWS4J可视编辑器,您应该遵循其定义XML限定名的格式,和前面描述的一样。清单8展示了CREATION WHILE的元素。
清单8:市场流程定义代码片断(DemoProcess.bpel)
<while name="CREATION_WHILE"
condition="bpws:getVariableData('status', 'status', '//type')=-1 or
bpws:getVariableData('status', 'status', '//type')=-2"
xmlns:demo="urn:demo:MarketplaceService"
xmlns:svc="http://bpeldemo.ibm.com/services/"> <sequence name="While_Sequence"> <receive name="Create_Receive" partner="Buyer" portType="demo:BuyerPT"
operation="create" variable="order" createInstance="yes">
<correlations>
<correlation set="identifier" initiate="yes"/>
</correlations>
</receive> <assign name="Status_Assign">
<copy>
<from variable="order" part="order" query="//status"/>
<to variable="status" part="status"/>
</copy>
</assign> <switch name="Validation_Switch">
<case condition="bpws:getVariableData('status', 'status', '//type')!=-3"> <sequence name="Validation_Sequence"> <invoke name="Validation_Invoke" partner="ValidationService"
portType="svc:ValidationUtilityPT" operation="validate"
inputVariable="order" outputVariable="status"/> <assign name="Sync_Assign">
<copy>
<from variable="status" part="status"/>
<to variable="order" part="order" query="//status"/>
</copy>
</assign> <invoke name="Sync_Invoke" partner="SynchronizationService"
portType="svc:SynchronizationUtilityPT" operation="synchronize"
inputVariable="order" outputVariable="order"/> <switch name="Calculation_Switch">
<case condition="bpws:getVariableData('status', 'status', '//type')=1"> <sequence name="Calculation_Sequence"> <invoke name="Calculation_Invoke" partner="CalculationService"
portType="svc:CalculationUtilityPT"
operation="calculate"
inputVariable="order" outputVariable="status"/>
<assign name="Sync_Assign">
<copy>
<from variable="status" part="status"/>
<to variable="order" part="order" query="//status"/>
</copy>
</assign> <invoke name="Sync_Invoke" partner="SynchronizationService"
portType="svc:SynchronizationUtilityPT" operation="synchronize"
inputVariable="order" outputVariable="order"/>
</sequence>
</case>
</switch>
</sequence>
</case>
</switch> <reply name="Create_Reply" partner="Buyer" portType="demo:BuyerPT"
operation="create" variable="status"/>
</sequence>
</while>
除了在CREATION_ SEQUENCE中创建订单之外,我们还需要获取买方的同意和卖方的批准。在我们的岩石中,两个活动作为分支进行实现:Agreement_Switch用于买方,而Approval_Switch用于卖方。请参见DemoDefaultProcess.bpel文件中的引用流程定义,以获得关于这些分支的详细情况。您可以采用两种方式来添加这些定义,第一种方式是通过BPWS4J可视界面(和前面的示例类似),第二种方式是在关闭CREATION_ SEQUENCE的</sequence>
标签之前,将它们手工复制和粘贴到BPEL4WS Source 中。
这是在我们的岩石中创建BPEL4WS定义的最后一步。您可以将自己创建的流程与DemoDefaultProcess.bpel文件中的引用流程定义进行比较。在BPWS4J编辑器中,您的流程应该如下所示:
记住,此图与我们在图1:市场流程高层结构中介绍的流程的初始高层结构相匹配. 让我们讨论我们可以如何使用描述和其他演示构件(artifact),来将流程部署到WebSphere Application Server(WAS)中并且运行演示。
部署和执行
将流程部署到WebSphere中
请执行以下步骤来将演示流程部署到WAS中。
- 将演示企业应用程序安装到WebSphere中
启动WAS服务器服务。打开WAS Administrative Console向导,并且使用Install New Application向导来从<demo_dir>/webapps目录中安装demo_process.ear文件。除了应该选取Pre-compile JSP选项之外,接受安装向导中的所有缺省值。将更改保存到主配置中。
在保存了更改之后,转到Enterprise Applications页,然后单击IBM developerWorks BPEL4WS Demo链接。接着单击Related Items下的Web Module,然后单击BPELEngine.war链接。将Classloader Mode特性更改为PARENT_LAST,并且单击OK按钮。将更改保存到主配置中。
- 定制流程
定位到<WAS_dir>/installedApps/<your_node_name>/<IBM developerWorks BPEL4WS Demo.ear>/<BPELEngine.war>/demo/。这里,<WAS_dir>是安装WebSphere Application Server的目录的名称(例如,d:\WebSphere\AppServer)。更改import中的URL和WSDL文件的soap:address元素,以匹配您的端口和主机(如果它们不同于缺省值http://localhost:80的话)。
定位到 <WAS_dir>/installedApps/<your_node_name>/<IBM developerWorks BPEL4WS Demo.ear>/<Marketplace.war>/web-inf/classes/。 更改soaplistener.properties中的soaplistener特性的URL,以匹配您的端口和主机(如果它们不同于缺省值http://localhost:80的话)。
- 启动应用程序
在WAS Administrative Console中,启动您的企业应用程序。如果您使用WebSphere with IBM HTTP Server,就可以通过WAS Administrative Console重新生成Web服务器插件并且重启Web服务器。
- 部署流程
要部署流程,可以定位到http://<your_host>:<your port>/bpws4j/admin/index.html。选择Deploy。对于流程定义,您可以提供引用流程定义(DemoDefaultProcess.bpel),也可以提供您在本教程中创建的流程定义。这里,我们又一次使用<WAS_dir>/installedApps/<your_node_name>/<IBM developerWorks BPEL Demo.ear>/<BPELEngine.war>/demo目录中的WSDL定义。选择流程WSDL描述(DemoProcess.wsdl)和BPEL4WS描述,然后单击Continue Deployment。选择合作伙伴Web服务描述的WSDL文件。它们的名称匹配合作伙伴的名称(例如,Assessment.wsdl匹配AssesmentService)。单击Start Serving the Process。
- 重启应用程序
在WAS Administrative Console,暂停您的企业应用程序,然后再次启动。
注意:这是一个必须遵循的步骤,否则演示将不能正常进行!
运行演示
打开位于http://<your_host>:<your port>/marketplace/的演示开始页。按照演示使用说明运行演示。
总结
Recap
在任何企业中建立业务流程并通过工作流的形式使其自动化,都可以减少风险并且降低维护成本。随着BPEL4WS的发展,您可以创建基于新兴的Web服务技术的实际工作流。在本教程中,我们简要地讨论了业务流程管理的某些重要方面和术语以及BPEL4WS语法和使用模式。我们通过演示市场场景来演示开发人员可以如何使用BPWS4J、WebSphere Application Server和WebSphere Studio Application Developer来创建、部署和运行基于BPEL4WS的工作流。
实现BPEL4WS演示:教程的更多相关文章
- [视频]K8飞刀 BadUSB Teensy自动种马演示教程
[视频]K8飞刀 Teensy USB自动种马演示教程 链接: https://pan.baidu.com/s/13bM1XSLrhlf90FDmPGfo1g 提取码: gy2q 源码:https:/ ...
- [视频]K8飞刀 exploit管理功能 演示教程
[视频]K8飞刀 exp管理功能 演示教程 链接: https://pan.baidu.com/s/1rYb3rh4od3j07TZAAq_smw 提取码: kksa
- [视频]K8飞刀 shellcode loader演示教程
[视频]K8飞刀 shellcode loader演示教程 https://pan.baidu.com/s/1eQ77lPw
- [视频]K8飞刀 SQL注入点脱库演示教程
K8飞刀 SQL注入点脱库演示动画教程 链接:https://pan.baidu.com/s/15gLTxiv9v1Te5QNFZnfV4A 提取码:eaa1
- NBearV3中文教程总目录
1.NBearV3 Step by Step教程——ORM篇 摘要:本教程演示如何基于NBearV3的ORM模块开发一个Web应用程序的全过程.本教程演示的实体关系包括:继承.1对1关联.1对多关联, ...
- Pro/E 5.0安装图解教程(也适用于Creo Elements/Pro 5.0)
安装前必读:☆ 本教程适用于 32 位 proe 5.0 M010,M020,M030,M040,M050,M060 过程完全一样:☆ 本教程用于 64 位 proe 5.0 M010,M020,M0 ...
- 【手把手教程】uniapp + vue 从0搭建仿微信App聊天应用:腾讯云TXIM即时通讯的最佳实践
基于uniapp + vue 实现仿微信App聊天应用实践,实现以下功能 1: 用户登陆 2: 聊天会话管理 3: 文本/图片/视频/定位消息收发 4: 贴图表情消息收发 5: 一对一语音视频在线通话 ...
- 【手把手教程】uniapp + vue 从0搭建仿斗鱼虎牙直播App:腾讯云MLVB移动直播实践连麦PK+带货
基于uniapp + vue 实现仿斗鱼虎牙腾讯云移动直播应用实践,实现以下功能 1: 用户登陆 2: 房间管理 3: 房间聊天 4: 直播美颜 5: Svga礼物动画 6: 一对一连麦观众 项目开发 ...
- Android模拟器部署历程
由于想玩一款手机的游戏,本人手机Android系统版本太低,不能安装.所以就想在WindowS上安装一个模拟器,然后安装游戏.想法挺好.实现起来确实经历了一个坎坷的过程.为了让其他人少走弯路,本人把此 ...
随机推荐
- JavaScript学习笔记-元素在滚动条滑动一定高度后自动置顶
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- 关于QString中的arg()函数使用方法
例:正确做法:ui->label->setText(QString("Processingfile%1").arg(index));错误做法: ui->label ...
- easyui-datagrid 报错:TypeError: col is null
一般是由于设置的属性用到的列,如: idField:'aa', sortName:'bb' 等在 columns:[[{field:'cc',width:80,title:'列cc'}, {field ...
- iOS开发小技巧--纯代码自定义cell
纯代码自定义cell 自定义cell的步骤(每个cell的高度不一样,每个cell里面显示的内容也不一样) 1.新建一个继承自UITableViewCell的子类 2.在initWithStyle:方 ...
- poj1182 带权并查集
食物链 Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 60225 Accepted: 17656 Description ...
- Mybatis 自动生成代码
准备条件: 将下面的文件放入同一目录下 操作步骤: 1/ 在 generatorConfig.xml 中配置相关的参数,与需要被自动生成的表 也可以 执行项目中的MybatisConfigAutoGe ...
- Fiddler环境配置教程
原理:安装Fiddler的电脑和将要进行检测的手机(iPhone.Android)加入同一局域网,这样手机上APP的请求就可以被电脑通过Fiddler抓取到. 局域网布置教程: 在将要布置局域网的电脑 ...
- 【Codeforces 723D】Lakes in Berland (dfs)
海洋包围的小岛,岛内的有湖,'.'代表水,'*'代表陆地,给出的n*m的地图里至少有k个湖,求填掉面积尽量少的水,使得湖的数量正好为k. dfs找出所有水联通块,判断一下是否是湖(海水区非湖).将湖按 ...
- 【OpenJ_POJ C16D】Extracurricular Sports(构造,找规律)
题目求n个互不相同的数,满足其和为其lcm.我们把lcm看成一个线段,分割成长度不同的n份.当然分法有很多,我们只需要构造一个好想好写的.先分成两个二分之一,取其中一个二分之一再分成1/3和2/3,接 ...
- eclipse下查看jdk源码
打开eclipse,点 "window"-> "Preferences" -> "Java" -> "Insta ...