jbpmAPI-6
第六章流程。
6.1. What is BPMN 2.0
业务流程模型和符号(BPMN)2.0规范是OMG规范,不仅定义了一个标准的业务流程的图形化表述(如BPMN 1. x),但现在还包括执行语义定义的元素,和一个XML格式如何存储和共享过程定义。
jBPM6允许您执行过程使用BPMN 2.0定义的XML格式。这意味着您可以使用所有不同的jBPM6工具模型、执行、管理和监控业务流程使用BPMN 2.0格式指定可执行业务流程。实际上,完整的BPMN 2.0规范还包括细节如何表示编排和协作。不过关注jBPM项目规范的一部分,它可以用来指定可执行流程。
可执行流程在BPMN包含不同类型的节点相互连接使用序列流。BPMN 2.0规范定义了三种类型的节点:
事件:他们是用来模拟特定事件的发生。这可能是一个开始事件(即用于指示流程)的开始,结束事件(定义过程的结束,或的子流)和中间事件(表明事件的执行过程中可能发生的)。
活动:这些定义不同的操作需要执行的执行过程。不同类型任务的存在,这取决于类型的活动你正在试图创建模型(如人工任务、服务任务等)和活动也可以嵌套(使用不同类型的子流程)。
网关:可用于定义多个路径。根据类型的网关,这些可能表明并行执行,选择,等等。
jBPM6没有实现所有的BPMN 2.0规范中定义的元素和属性。我们所做的不过一个重要子集的支持,包括最常见的节点类型,可以使用内部可执行流程。这包括(几乎)所有的元素和属性的“共同执行”子类中定义BPMN 2.0规范,通过一些额外的元素和属性扩展我们相信在这种情况下是有价值的。元素和属性的完整支持下面可以找到,但它包含元素,如:
流对象
事件
启动事件(条件信号,没有任何消息,计时器)
结束事件(终止,没有错误,升级、信号信息,补偿)
中间捕捉事件(信号,定时器,条件,消息)
中间把事件(没有,信号、升级、消息、补偿)
Non-interrupting边界事件(升级、信号、定时器、条件、消息)
打断边界事件(升级、误差、信号、定时器、条件信息,补偿)
活动
脚本的任务
任务
服务任务
用户任务
业务规则任务
人工任务
发送任务
接受任务
可重用的子流程(呼叫活动)
嵌入式子流程
事件子流程
特别的子流程
数据对象
网关
不同
独家
包容
平行
基于事件的
融合
独家
包容
平行
车道
数据
Java类型的语言
流程属性
嵌入式子流程属性
活动属性
连接对象
序列流
例如,考虑下面的“Hello World”BPMN 2.0的过程,没有更多,写出一个“Hello World”过程开始时声明。
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="Definition"
targetNamespace="http://www.example.org/MinimalExample"
typeLanguage="http://www.java.com/javaTypes"
expressionLanguage="http://www.mvel.org/2.0"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:tns="http://www.jboss.org/drools"> <process processType="Private" isExecutable="true" id="com.sample.HelloWorld" name="Hello World" > <!-- nodes -->
<startEvent id="_1" name="StartProcess" />
<scriptTask id="_2" name="Hello" >
<script>System.out.println("Hello World");</script>
</scriptTask>
<endEvent id="_3" name="EndProcess" >
<terminateEventDefinition/>
</endEvent> <!-- connections -->
<sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
<sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" /> </process> <bpmndi:BPMNDiagram>
<bpmndi:BPMNPlane bpmnElement="Minimal" >
<bpmndi:BPMNShape bpmnElement="_1" >
<dc:Bounds x="15" y="91" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_2" >
<dc:Bounds x="95" y="88" width="83" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" >
<dc:Bounds x="258" y="86" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_1-_2" >
<di:waypoint x="39" y="115" />
<di:waypoint x="75" y="46" />
<di:waypoint x="136" y="112" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_2-_3" >
<di:waypoint x="136" y="112" />
<di:waypoint x="240" y="240" />
<di:waypoint x="282" y="110" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram> </definitions> 创建自己的流程使用BPMN 2.0格式,你可以
jBPM设计师是一个开源的基于web的编辑器,支持BPMN 2.0格式。我们已经嵌入到jbpm控制台BPMN 2.0过程可视化和编辑。您可以使用设计师(独立或集成)来创建/编辑BPMN 2.0流程,然后将它们导出BPMN 2.0格式或将它们保存到存储库和导入它们,这样他们就可以被执行。
正在创建一个新的BPMN2 Eclipse插件来支持完整的BPMN2规范。
你可以手动创建BPMN 2.0过程直接通过编写XML文件。您可以验证您的过程的语法对BPMN 2.0 XSD,或使用Eclipse插件的验证器检查语法和完整性的模型。 创建一个新的流程文件使用Drools Eclipse插件向导,向导的最后一页,确保你选择Drools 5.1代码的兼容性。这将创建一个新的流程使用BPMN 2.0的XML格式。但是要注意,这并不完全是一个BPMN 2.0编辑器,因为它仍然使用不同的属性名称等。然而它保存该流程使用有效的BPMN 2.0语法。还请注意,编辑器不支持所有节点类型和属性,已经支持执行引擎。 下面的代码片段显示了如何BPMN2过程加载到你的知识库…
private static KnowledgeBase createKnowledgeBase() throws Exception {
KieHelper kieHelper = new KieHelper();
KieBase kieBase = kieHelper
.addResource(ResourceFactory.newClassPathResource("sample.bpmn2"))
.build(); return kieBase;
} . .以及如何执行这个过程……
KieBase kbase = createKnowledgeBase();
KieSession ksession = kbase.newKieSession();
ksession.startProcess("com.sample.HelloWorld");
6.2. Process
业务流程是一个描述的顺序图需要执行一系列步骤,使用流程图。集合的过程由互相联系的节点使用连接。每个节点代表一个步骤在整个过程连接指定如何从一个节点到另一个过渡。一个大的选择预定义的节点类型定义。本章描述了如何定义这些过程和在应用程序中使用它们。
6.2.1。创建一个过程
进程可以创建通过使用以下三种方法之一:
使用图形化的流程编辑器如jBPM网页设计师或Eclipse BPMN2 modeler
作为一个XML文件,根据XML过程定义格式的XML模式定义BPMN 2.0规范。
通过直接创建一个进程使用API。
6.2.1.1。使用图形化的BPMN2编辑器
图形BPMN2编辑器是一个编辑器,允许你创建一个过程通过将不同节点拖画布上和编辑这些节点的属性。图形BPMN2 modeler是一个Eclipse插件托管在eclipse.org网站提供的数量,其中一个是jBPM项目。一旦你建立了一个jBPM项目(见安装程序创建Eclipse环境中工作,你就可以开始),就可以开始添加流程。在一个项目中,启动“新”向导(使用Ctrl + N)或右键单击目录你想把你的过程,并选择“新”,然后“文件”。给文件名称和扩展bpmn(例如MyProcess.bpmn)。这将打开流程编辑器(您可以安全地忽略警告文件无法读取,这只是因为文件仍然是空的)。
首先,确保你能看到Properties视图Eclipse窗口的底部,因为它会需要填写流程中的不同属性的元素。如果你不能看到properties视图,使用菜单“窗口”打开它,然后“显示视图”和“其他……”,并在“将军”文件夹中选择Properties视图。
流程编辑器由一个调色板,画布和大纲视图。新元素添加到画布,选择您想创建的元素的调色板,然后将它们添加到画布上,单击首选位置。例如,单击“结束事件”图标面板的GUI。点击流程中的一个元素允许您设置元素的属性。您可以连接节点(只要是允许不同类型的节点)通过使用“序列流”从调色板中。
你可以继续添加节点和连接过程,直到它代表你想指定的业务逻辑。
6.2.1.2。使用XML定义流程
还可以指定流程直接使用底层的BPMN 2.0 XML。这些XML过程定义的语法使用BPMN 2.0的XML模式定义。例如,下面的XML片段显示了一个简单的过程,它包含一个序列开始的事件,一个脚本的任务,将“Hello World”打印到控制台,和结束事件。
<?xml version="1.0" encoding="UTF-8"?>
<definitions id="Definition"
targetNamespace="http://www.jboss.org/drools"
typeLanguage="http://www.java.com/javaTypes"
expressionLanguage="http://www.mvel.org/2.0"
xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"Rule Task
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
xmlns:g="http://www.jboss.org/drools/flow/gpd"
xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
xmlns:tns="http://www.jboss.org/drools"> <process processType="Private" isExecutable="true" id="com.sample.hello" name="Hello Process" > <!-- nodes -->
<startEvent id="_1" name="Start" />
<scriptTask id="_2" name="Hello" >
<script>System.out.println("Hello World");</script>
</scriptTask>
<endEvent id="_3" name="End" >
<terminateEventDefinition/>
</endEvent> <!-- connections -->
<sequenceFlow id="_1-_2" sourceRef="_1" targetRef="_2" />
<sequenceFlow id="_2-_3" sourceRef="_2" targetRef="_3" /> </process> <bpmndi:BPMNDiagram>
<bpmndi:BPMNPlane bpmnElement="com.sample.hello" >
<bpmndi:BPMNShape bpmnElement="_1" >
<dc:Bounds x="16" y="16" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_2" >
<dc:Bounds x="96" y="16" width="80" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="_3" >
<dc:Bounds x="208" y="16" width="48" height="48" />
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="_1-_2" >
<di:waypoint x="40" y="40" />
<di:waypoint x="136" y="40" />
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="_2-_3" >
<di:waypoint x="136" y="40" />
<di:waypoint x="232" y="40" />
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram> </definitions> 处理XML文件由两部分组成,前一部分(“过程”元素)包含不同节点的定义及其性质,下部(“BPMNDiagram”元素)包含所有的图形信息,节点的位置。处理XML包含一个<流程>元素。该元素包含参数相关过程(其类型、名称、id和包名称),并由三部分组成:头部分(流程级信息如变量、全局变量、进口和车道可以定义),定义每个节点的节点部分在这个过程中,和一个连接部分包含所有节点之间的连接。在节点部分,为每个节点有一个特定的元素,定义各种参数,可能的话,子元素节点类型
Figure 6.3. The different types of BPMN2 events |
Figure 6.4. The different types of BPMN2 activities and gateways |
6.2.1.3。细节:流程属性
BPMN2过程是一个流程图,不同类型的节点链接使用连接。这个过程本身暴露以下属性:
Id:过程的惟一Id。
名字:流程的显示名称。
版本:版本号的过程。
图6.5。BPMN2流程属性
除此之外也可以定义如下:
变量:变量可以定义的执行过程中存储数据。看到部分”? ? ?”详情。
6.3. Activities
6.3.1. Script task
图6.7。脚本的任务
代表了一个脚本,在这一过程中应该执行。一个脚本任务应该有一个传入的连接和一个即将离任的连接。指定应该执行相关操作,编码操作(即使用的方言。、Java或MVEL)和实际操作代码。这段代码可以访问任何变量和全局变量。还有一个预定义的变量kcontext引用ProcessContext对象(例如,它可以被用来访问当前ProcessInstance或NodeInstance,和获取并设置变量,或进入ksession使用kcontext.getKieRuntime())。当达到一个脚本的任务是在此过程中,它将执行动作,然后继续下一个节点。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
行动:与该操作关联的动作脚本节点。
请注意,您可以编写一个脚本节点内部任何有效的Java代码。这个基本上允许你做的任何事情,在这样的一个脚本节点。然而有一些警告:
当试图创建一个更高级的业务过程,业务用户,也应该理解,这可能是明智的避免低层次的内部实现细节的过程,包括在这些脚本的任务。脚本任务仍可以用来快速操纵变量等,但等概念服务任务可以用于模型更复杂的行为在一个更高级的方式。
脚本应该立即。他们使用引擎线程来执行脚本。执行脚本,可能需要一些时间应该是建模为一个异步服务的任务。
你应该尽量避免接触外部服务通过一个脚本节点。这通常不仅违反前两个警告,也没有的知识与外部服务交互引擎,这可能会产生问题,特别是当使用持久性和事务。一般来说,这可能是明智的使用服务模型与外部的沟通服务的任务。
脚本不应该抛出异常。运行时异常应该抓住,例如管理内部的脚本或转换成信号或错误,然后可以处理内部的过程。
6.3.2. Service task
图6.8。服务任务
代表一个(抽象)的工作单元,在这一过程中应该执行。外的所有工作执行的流程引擎应该代表(在声明方式)使用服务的任务。不同类型的服务是预定义的,如。消息、发送电子邮件、日志记录等,用户可以定义特定于域的服务或工作项,使用一个唯一的名称,通过定义参数(输入)和结果(输出)与这种类型的相关工作。检查这一章领域特定的流程详细的解释和说明的例子如何定义和使用工作项流程。当一个服务任务是达到在这个过程中,执行相关的工作。服务任务应该有一个传入的连接和一个即将离任的连接。
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
参数映射:允许过程变量的值复制到工作项的参数。在创建工作项的值将被复制。
结果映射:允许复制结果参数的值的工作项流程变量。每种类型的工作可以定义结果参数后,(可能)会返回工作项已经完成。结果映射可用于给定的结果参数的值复制到给定的变量在这一过程中。例如,“FileFinder”工作项返回一个给定的搜索条件匹配的文件列表内的结果参数文件。这个列表文件可以被绑定到一个过程变量在该流程中使用。在完成的工作项,将复制的值。
入境和出境的行动:行动执行此节点进入或退出后,分别。
额外参数:每种类型的工作项可以定义额外的参数相关的这种类型的工作。例如,“电子邮件”工作项定义了额外的参数等,,Subjectand身体。用户可以直接提供这些参数值,或定义一个参数映射,将复制给定变量的值在这个过程中给定的参数,如果指定了两个,将优先级的映射。参数类型的字符串可以使用# { }表达式嵌入一个值的字符串。检索的值将被创建的工作项时,将取代和替换表达式调用toString()的结果变量。表达式可能仅仅是一个变量的名称(在这种情况下,它解析为变量的值),但更先进的MVEL表达式是可能的,例如,# { person.name.firstname }。
6.3.3. User task
图6.9。用户任务
过程也涉及人类演员需要执行的任务。用户要执行的任务代表一个原子被人类演员。它应该有一个传入的连接和一个即将离任的连接。用户任务可以结合使用泳道多个人工任务分配给类似的演员。请参阅章对人工任务的更多细节。只不过一个用户任务实际上是一种特定类型的服务节点(“人工任务”类型的)。一个用户任务包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
TaskName:人工任务的名称。
优先级:一个整数表示人工任务的优先级。
评论:评论与人工任务相关。
ActorId:演员负责执行人工任务的id。演员可以指定id的列表使用逗号(,)分隔符。
GroupId:负责执行的组id的人工任务。组id的列表可以指定使用一个逗号(,)分隔符。
Skippable:指定人工任务是否可以跳过,即。,演员是否可能决定不执行任务。
内容:与该任务相关的数据。
泳道:泳道此人工任务节点的一部分。泳道可以很容易将多个人工任务分配给相同的演员。看到更多细节的人工任务章如何使用泳道。
入口和出口的行动:行动执行脚本,在入口和出口的节点,分别。
参数映射:允许过程变量的值复制到参数的人工任务。在创建的人工任务,将复制的值。
结果映射:允许人工任务的结果参数的值复制到一个流程变量。人工任务的完成,将复制的值。一个人工任务的结果变量“结果”,包含人类演员返回的数据。变量“ActorId”包含的id的演员,实际执行该任务。
应该定义一个用户任务需要执行的任务类型(像TaskName使用属性,评论等等),谁需要执行它(使用actorId或groupId)。请注意,如果有数据与此相关的特定流程实例,最终用户需要执行任务时,这个数据应该作为任务的内容传递。任务例如没有访问流程变量。看看这一章人工任务来获得更多的细节如何人工任务和流程实例之间传递数据。
6.3.4. Reusable sub-process
代表另一个进程在这个过程的调用。子流程节点应该有一个传入的连接和一个即将离任的连接。当一个可重用的子流程节点到达在这个过程中,发动机将开始给定id的过程。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
ProcessId:进程的id应该被执行。
等待完成(默认情况下如此):如果这个属性是真的,这个子流程节点只会继续如果开始的子进程终止其执行(完成或中止);否则,它将继续后立即启动子流程(所以它不会等待其完成)。
独立(默认情况下如此):如果这个属性是真的,子进程是开始作为一个独立的过程,这意味着子进程将不会终止如果父进程完成(或子流程节点取消其他原因);否则,活跃的子流程将被取消在父进程的终止子流程节点(或取消)。请注意,您只能独立设置为“false”只有在“等待完成”设置为true。
入境和出境的行动:行动执行此节点进入或退出后,分别。
参数/映射:子流程节点还可以定义为变量,out-mappings。变量的“在”的映射将被用作参数(相关的参数名称)在开始这个过程。变量定义的子进程的“出去”映射将复制到这个过程的变量时,子进程已经完成。请注意,您可以使用“out”映射只有当“等待完成”设置为true。
6.3.5. Business rule task
业务规则任务代表一组规则,需要评估。规则评估当节点。一个规则的任务应该有一个传入的连接和一个即将离任的连接。单独的文件中定义的规则是使用Drools规则格式。规则可以成为一个特定的规则流的一部分使用ruleflow-group属性组标题的规则。
当一个规则的任务是达到在这个过程中,发动机将开始执行规则的相应ruleflow-group(如果有的话)。会自动继续执行下一个节点如果没有更积极的规则在本规则流组。因此,规则流组的执行期间,新激活属于当前活动规则流组可以添加到议事日程由于其他规则的事实所做的更改。立即注意过程将继续下一个节点,如果遇到一个规则流组没有活跃的规则。
如果规则流集团已经活跃,规则流集团将保持活跃和执行只会继续如果所有活动规则的规则流组已经完成。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
RuleFlowGroup:规则流组的名称代表的规则集的RuleFlowGroup节点。
6.3.6. Embedded sub-process
图6.12。嵌入式子流程
子流程是一个节点,其他节点可以包含容器,它作为一个节点。这不仅允许的嵌入过程的一部分在这样的子流程节点,还额外变量的定义,所有节点都可以访问这个容器内。子流程应该有一个传入的连接和一个即将离任的连接。它还应该包含一个开始节点,定义从哪里开始(在子流程)当你到达子流程。它还应该包含一个或多个事件结束。注意,如果您使用一个终止事件节点内的子流程,你终止子进程。子流程结束时没有更积极在子流程节点。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
可以定义变量:额外的变量来存储数据在这个节点的执行。看到部分”? ? ?”详情。
6.3.7. Multi-instance sub-process
图6.13。多实例子流程
多个实例子流程是一种特殊的子流程,允许您执行包含过程段多次,一次集合中的每个元素。多个实例子流程应该有一个传入的连接和一个即将离任的连接。等待直到完成嵌入式流程片断给定集合的每个元素在继续之前。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
CollectionExpression:一个变量的名称代表的集合的元素应该迭代结束。类型的变量应该是一个数组或集合java.util.Collection。如果集合表达式的求值结果为null或空集合,子流程将立即完成多个实例和遵循它的输出连接。
VariableName:变量的名称包含当前元素的集合。这让节点在复合节点访问所选元素。
CollectionOutput:一个变量的名称代表的元素的集合将收集所有输出多实例的子过程
OutputVariableName:变量的名称包含currentl输出多实例活动
CompletionCondition:MVEL表达式,将评估每个实例完成检查如果多实例活动已经可以完成。以防它评估为true剩余所有其他实例在多实例活动将被取消。
6.4. Events
6.4.1. Start event
图6.14。启动事件
这一过程的开始。流程应该有一个开始节点(没有开始节点没有事件定义),不能到达的连接,应该有一个外向的连接。每当开始一个过程,在这个节点和执行将开始自动继续第一节点与这个事件开始,等等。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
6.4.2. End events
这一过程的终结。进程应该有一个或多个事件结束。最后事件应该有一个传入连接,不能有任何传出连接。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
终止:结束事件可以终止整个进程或路径。流程实例时终止,这意味着其状态设置为完成和所有其他节点,可能仍然活跃在此流程实例(在并行路径)取消了。终止结束事件只是对这条道路(本分支的执行将在这里结束),但其他并行路径仍然可以继续下去。流程实例将自动完成如果没有更积极的内部路径,流程实例(例如,如果一个流程实例终止结束节点但没有达到一个更积极的分支机构内部的流程实例,将完成流程实例)。终止结束事件可视化使用一个完整的圈内事件节点,终止事件节点是空的。注意,如果您使用一个终止事件节点内的子流程,你终止子流程和顶级仍在继续。
6.4.2.2. Throwing error event
一个错误事件可用于信号的异常条件的过程。它应该有一个传入连接,没有传出连接。当一个错误事件到达在这个过程中,它会抛出一个错误的名字。过程将寻找一个适当的错误处理程序能够处理这种故障。如果没有找到错误处理程序,流程实例将中止。一个错误事件包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
FaultName:错误的名称。这个名字是用来寻找适当的异常处理程序能够处理这种故障。
FaultVariable:包含数据的变量的名称与此相关的错。这个数据也转嫁到异常处理程序(如果找到一个)。
错误处理程序可以指定使用边界事件。
6.4.3. Intermediate events
多次代表一个计时器,可以触发一个或后一个给定的一段时间。一个计时器事件应该有一个传入的连接和一个即将离任的连接。计时器延迟指定计时器应该等多久才第一次触发。当一个计时器事件到达在这个过程中,它将启动相关的计时器。取消,如果取消计时器节点计时器(如。通过完成或中止封闭流程实例)。参考部分”? ? ?”的更多信息。计时器事件包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
定时器延时:节点的延迟应该等待第一次触发。表单的表达式应该[# d][# h][# m][# s][#(女士)]。这允许您
6.4.3.2. Catching signal event
一个信号事件可以用来响应内部或外部事件的执行过程。一个信号事件应该有一个传入的连接和一个即将离任的连接。它指定的事件类型。每当检测到这种类型的事件时,节点连接的节点将会触发此事件。它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
EventType:预计的事件的类型。
VariableName:变量的名称,将包含与此事件相关的数据(如果有的话)当这个事件发生时。
一个流程实例可以表示一个特定的事件发生
ksession.signalEvent(eventType, data, processInstanceId) 这将触发所有(积极)信号事件节点的流程实例,正在等待事件类型。事件相关的数据可以通过使用数据参数。如果事件节点指定一个变量名,这个数据将被复制到该变量当事件发生时。
还可以使用事件节点内部子过程。这些事件节点只能然而子流程时积极活跃。
你也可以生成一个信号从一个流程实例。一个脚本(在脚本任务进入或退出操作或使用)可以使用
kcontext.getKieRuntime().signalEvent(eventType, data, kcontext.getProcessInstance().getId()); 把信号事件也可以用于模型的信号事件。
6.5. Gateways
允许您创建分支机构在你的过程。不同网关应该有一个传入连接和两个或两个以上的传出连接。目前有三种类型的网关节点支持:
或平行意味着同时控制流将继续在所有对外的连接。
XOR或独家意味着一个即将离任的连接将被选中。这一决定是由评估约束与每个即将离任的连接。优先级低的约束选择评估为true。约束可以指定使用不同的方言。注意,您应该总是确保至少一个即将离任的连接在运行时将评估为true(引擎在运行时将抛出一个异常,如果它不能找到至少一个即将离任的连接)。
或包容性意味着所有对外的连接的选择条件的求值结果为true。条件相似的独家门户,除了没有优先考虑。注意,您应该确保至少一个即将离任的连接在运行时将评估为true,因为引擎会在运行时抛出异常,如果不能确定一个外向连接。
它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
类型:分裂节点的类型,即,XOR或(见上图)。
约束:约束与每一个即将离任的连接(独家或包容性的网关)。
6.5.2. Converging gateway
允许你同步多个分支机构。融合网关应该有两个或两个以上的传入连接和一个传出连接。目前有三种类型的分裂支持:
或平行意味着将等到所有传入分支在继续之前完成。
XOR或独家意味着它就继续其传入分支之一已经完成。如果从多个触发传入的连接,它将为每个触发器的触发下一个节点。
或包容就意味着它继续其所有直接激活路径传入分支已经完成。这是复杂的合并行为BPMN2规范中描述,但在大多数情况下这意味着或加入将等待所有活动流始于或分裂。一些先进的案例(包括其他网关之间的或重复的计时器)将导致不同的“直接活动路径”计算。
它包含以下属性:
Id:节点的Id(这是独特的一个节点内容器)。
名字:节点的显示名称。
类型:加入节点的类型,例如,或异或。
6.6. Others
6.6.1. Variables
用于存储运行时数据,在执行的过程中,可以使用流程变量。一个变量被定义为一个名称和一个数据类型。这可能是一个基本的数据类型,如布尔,int,字符串,或任何类型的子类对象(它必须实现Serializable接口)。变量可以定义在一个变量的范围。顶级范围的变量范围过程本身。方法使用子流程可以被定义。变量定义在一个子只是访问节点的范围内。
访问一个变量时,这一过程将寻找合适的变量范围,定义了变量。嵌套变量作用域是被允许的。一个节点将永远寻找父容器的一个变量。如果变量不能被发现,它将会在父容器,等等,直到达到流程实例本身。如果变量不能被发现,一个读访问收益率null,和写访问产生一条错误消息,流程继续执行。
变量可用于各种方式:
流程级变量可以设置当开始一个过程通过提供地图startProcess的调用方法的参数。这些参数将被设置为变量的过程范围。
脚本操作可以直接访问变量,只需使用变量的名称作为一个局部参数的脚本。例如,如果“org.jbpm的过程定义了一个变量的类型。人”在这个过程中,一个脚本的过程中可以直接访问这个:
person.setAge(10); 改变一个变量的值在一个脚本可以通过知识背景:
kcontext.setVariable(variableName, value);
服务任务(和可重用的子流程)可以通过向外界过程变量的值(或另一个流程实例)通过变量映射到一个输出参数。例如,服务的参数映射任务可以定义流程变量x的值应该被映射到一个任务参数y之前被调用服务。你也可以注入过程变量的值硬编码的参数字符串使用# { }表达式。例如,一个人工任务的描述可以被定义为需要联系人# { person.getName()}(人是一个过程变量),将取代这个表达式的实际名称的人当需要调用服务。同样的服务结果(或可重用的子流程)也可以复制回一个变量使用映射的结果。
各种各样的其他节点也可以访问数据。事件节点例如可以到事件相关的数据存储在一个变量,等检查的属性不同的节点类型的更多信息。
可以访问流程变量也来自应用程序的Java代码。它是通过铸造ofProcessInstance WorkflowProcessInstance。看下面的例子:
最后,流程(和规则)都可以访问全局变量,即会话中定义全局变量和数据的知识。全局变量在行动就像直接访问变量。全局变量需要被定义为过程的一部分才可以使用。例如可以定义全局变量时,通过单击全局按钮指定一个动作脚本在Eclipse动作属性编辑器。你也可以设置一个全球的价值使用ksession从外面。setGlobal(名称、值)或从内部流程脚本usingkcontext.getKieRuntime().setGlobal(name,value);。
6.6.2. Scripts
动作脚本可用于不同的方式:
在一个脚本的任务,
进入或退出行为,与一些节点。
操作访问全局变量和变量定义的过程和预定义的variablekcontext。可以使用这个变量的类型是ProcessContext和几个任务:
获得当前节点实例(如果适用的话)。节点实例可以查询数据,如它的名字和类型。你也可以取消当前节点实例。
variable = ((WorkflowProcessInstance) processInstance).getVariable("variableName"); 列出所有流程变量见下面的代码片段:
org.jbpm.process.instance.ProcessInstance processInstance = ...;
VariableScopeInstance variableScope = (VariableScopeInstance) processInstance.getContextInstance(VariableScope.VARIABLE_SCOPE);
Map<String, Object> variables = variableScope.getVariables();
请注意,当您使用持久性,那么你必须使用基于命令的方法获得所有过程变量:
Map<String, Object> variables = ksession.execute(new GenericCommand<Map<String, Object>>() {
public Map<String, Object> execute(Context context) {
KieSession ksession = ((KnowledgeCommandContext) context).getStatefulKnowledgesession();
org.jbpm.process.instance.ProcessInstance processInstance = (org.jbpm.process.instance.ProcessInstance) ksession.getProcessInstance(piId);
VariableScopeInstance variableScope = (VariableScopeInstance) processInstance.getContextInstance(VariableScope.VARIABLE_SCOPE);
Map<String, Object> variables = variableScope.getVariables();
return variables;
}
});
NodeInstance node = kcontext.getNodeInstance();
String name = node.getNodeName(); 获得当前流程实例。可以查询流程实例数据(名称、id processId等等),流产或暗示的内部事件。
ProcessInstance proc = kcontext.getProcessInstance();
proc.signalEvent( type, eventObject ); 获取或设置变量的值。
访问知识运行时允许你做事情喜欢开始一个过程,信号(外部)事件,插入数据等。
Java和MVEL jBPM目前支持两种方言。Java动作应该有效的Java代码。MVEL MVEL动作可以使用业务脚本语言来表达。MVEL接受任何有效的Java代码,但另外支持嵌套访问的参数(例如,person.name而不是person.getName()),和许多其他脚本改进。因此,MVEL表达式是为业务用户更方便。例如,一个动作,打印出人的名字“请求者”变量的过程应该像这样:
// Java dialect
System.out.println( person.getName() ); // MVEL dialect
System.out.println( person.name );
6.6.3. Constraints
约束可以用在不同的地方在你的过程中,例如在一个不同的网关。jBPM支持两种类型的约束:
代码约束是布尔表达式,计算直接当他们到达。我们目前支持两种方言来表示这些代码约束:Java和MVEL。Java和MVEL约束直接访问代码中定义的全局变量和变量的过程。这是一个有效的Java代码的一个例子约束,人作为一个变量在此过程中:
return person.getAge() > 20; 一个类似的例子,一个有效的约束MVEL代码是:
return person.age > 20; 规则约束等于正常Drools规则条件。他们使用Drools规则语言语法的表达可能是复杂的约束。像任何其他规则,这些规则可以指工作记忆中的数据。他们也可以直接引用全局变量。这是一个有效的规则约束的一个例子:
Person( age > 20 ) 这对一个人测试20岁以上的工作记忆。
规则约束没有直接访问内部定义的变量的过程。然而可以引用当前流程实例在一个规则约束,通过将流程实例添加到流程实例的工作记忆和匹配您的规则约束。我们添加了特殊类型的逻辑变量,以确保processInstance WorkflowProcessInstance只会匹配当前流程实例和工作记忆中的其他流程实例。注意,你不过是自己负责流程实例插入到会话,可能更新它,例如,使用Java代码或入境、出境或者明确的行动过程。下面的示例规则的约束将搜索的人名称相同的值存储在变量“name”的过程:
processInstance : WorkflowProcessInstance()
Person( name == ( processInstance.getVariable("name") ) )
# add more constraints here ...
6.6.4. Timers
计时器等一个预定义的时间,触发前,一次或多次。他们可以用来触发某些逻辑一段时间后,定期或重复一些动作。
6.6.4.1. Configure timer with delay and period
设置一个计时器节点延迟一段时间。延迟指定的时间等待节点之前激活后第一次触发计时器。随后触发激活之间定义了时间。一段在一次性定时器0的结果。
(时间和延迟)表达式应该表单的[# d][# h][# m][# s][#(女士)]。你可以指定数量的天,小时,分钟,秒和毫秒(这是默认的,如果你不指定任何东西)。例如,表达式“1小时”将等待1小时前触发计时器(再一次)。
6.6.4.2. Configure timer with CRON like expression
计时器事件时可以使用CRON配置像表达timeCycle用作计时器事件定义。重要的是,timeCycle定义的语言属性必须设置cron。这种循环的一个计时器控制以同样的方式作为CRON作业。CRON像支持表达式:
启动事件计时器
中间事件计时器
边界事件计时器
下面是一个例子,一个定义的边界计时器等CRON表达式
<bpmn2:boundaryEvent id="1" name="Send Update Timer" attachedToRef="_77A94B54-8B7C-4F8A-84EE-C1D310A343A6" cancelActivity="false">
<bpmn2:outgoing>2</bpmn2:outgoing>
<bpmn2:timerEventDefinition id="_erIyiJZ7EeSDh8PHobjSSA">
<bpmn2:timeCycle xsi:type="bpmn2:tFormalExpression" id="_erIyiZZ7EeSDh8PHobjSSA" language="cron">0/1 * * * * ?</bpmn2:timeCycle>
</bpmn2:timerEventDefinition>
</bpmn2:boundaryEvent> 这个计时器将火每一秒和活动将持续到这个边界事件附加到活跃。
6.6.4.3. Configure timer ISO-8601 date format
从版本6计时器可以配置与有效ISO8601日期格式,同时支持一个定时器和可重复的计时器。计时器可以定义为表示日期和时间,时间或重复间隔
日期- 2013 - 12 - 24 - t20:00:00.000 + 02:00 -火灾在圣诞除夕晚上8点
- pt1火灾后持续时间1秒
每秒钟可重复间隔- R / pt1火灾,没有限制,或者R5 / pt1将火每秒钟5倍
6.6.4.4. Configure timer with process variables
除了上面两个配置选项可以指定计时器使用流程变量,可以由字符串表示的醚延迟和时期或ISO8601日期格式。通过指定# {变量}引擎将动态提取过程变量,并把它作为定时器表达式。
定时器服务负责确保计时器会在适当的时候触发。定时器也可以取消,也就是说,计时器将不再被触发。
计时器可用于两种方法在一个过程:
一个计时器事件可能被添加到流程。其激活启动计时器,触发时,一次或多次,它激活定时器节点的继任者。随后,即将离任的连接一个计时器的一个积极的时期多次触发。取消一个计时器节点还取消相关的定时器,然后不再触发就会发生。
计时器可以关联到一个边界事件子流程或任务。
6.7. Process Fluent API
同时建议使用图形化编辑器或者底层XML定义流程(保护自己免受内部API),也可以直接使用过程API定义一个过程。最重要的流程模型元素中定义的包org.jbpm.workflow.core andorg.jbpm.workflow.core.node。提供了“流利的API”,允许您轻松地构建过程以一种可读的方式使用工厂。最后,您可以验证你是手动构建的过程。
6.7.1. Example
这是一个简单的示例脚本的任务只有一个基本过程:
RuleFlowProcessFactory factory =
RuleFlowProcessFactory.createProcess("org.jbpm.HelloWorld");
factory
// Header
.name("HelloWorldProcess")
.version("1.0")
.packageName("org.jbpm")
// Nodes
.startNode(1).name("Start").done()
.actionNode(2).name("Action")
.action("java", "System.out.println(\"Hello World\");").done()
.endNode(3).name("End").done()
// Connections
.connection(1, 2)
.connection(2, 3);
RuleFlowProcess process = factory.validate().getProcess(); KieServices ks = KieServices.Factory.get();
KieFileSystem kfs = ks.newKieFileSystem();
Resource resource = ks.getResources().newByteArrayResource(
XmlBPMNProcessDumper.INSTANCE.dump(process).getBytes());
resource.setSourcePath("helloworld.bpmn2");
kfs.write(resource);
ReleaseId releaseId = ks.newReleaseId("org.jbpm", "helloworld", "1.0");
kfs.generateAndWritePomXML(releaseId);
ks.newKieBuilder(kfs).buildAll();
ks.newKieContainer(releaseId).newKieSession().startProcess("org.jbpm.HelloWorld"); 您可以看到,我们开始通过调用静态createProcess()方法从RuleFlowProcessFactory类。这个方法创建一个新的流程具有给定id并返回RuleFlowProcessFactory,可用于创建过程。一个典型的过程由三个部分组成。标题部分包含全局元素像进程的名称,进口,变量等。节点部分包含所有不同的节点,是过程的一部分。连接部分最后彼此连接这些节点创建一个流程图。
在这个例子里,标题包含名称和版本的过程和包名称。之后,你可以开始添加节点到当前进程。如果你有自动完成你可以看到,你有不同的方法来创建每个支持的节点类型在你的处置。 当你开始将节点添加到流程,在这个例子中通过调用startNode(),actionNode()andendNode()方法,您可以看到,这些方法返回一个特定NodeFactory,允许您设置该节点的属性。一旦你已经完成了配置,特定的节点,完成()方法返回您当前RuleFlowProcessFactory所以你可以添加更多的节点,如果必要的。
当你完成添加节点时,您必须联系他们通过创建它们之间的连接。可以通过调用方法连接,将链接之前创建的节点。
最后,您可以验证通过调用validate()方法生成的过程和检索createdRuleFlowProcess对象。
6.8. Testing
尽管业务流程不是代码(我们甚至建议你让他们尽可能高,避免添加实现细节),他们也有生命周期与其他发展文物。由于业务流程可以动态更新,测试它们(这样做时,你不要打破任何用例修改)是非常重要的。
6.8.1。单元测试
当单元测试你的过程,你测试过程是否按预期的行为在特定的用例,例如测试基于现有的输入输出。简化单元测试、jBPM包括一个助手类称为JbpmJUnitBaseTestCase(jbpm-test模块),您可以使用它们来大大简化你的JUnit测试,通过提供:
助手方法来创建一个新的RuntimeManager和RuntimeEngine对于一个给定的(套)过程(es)
您可以选择是否要使用持久性
断言语句检查
流程实例的状态(活跃,完成,流产)
哪个节点实例目前活跃
哪些节点触发(检查之后的路径)
把变量的值
例如,考虑下面的“hello world”程序包含一个事件开始,任务和结束事件的脚本。下面的JUnit测试将创建一个新会话,启动流程,然后验证流程实例是否成功完成这三个节点是否被执行。
public class ProcessPersistenceTest extends JbpmJUnitBaseTestCase { public ProcessPersistenceTest() {
// setup data source, enable persistence
super(true, true);
} @Test
public void testProcess() {
// create runtime manager with single process - hello.bpmn
createRuntimeManager("hello.bpmn");
// take RuntimeManager to work with process engine
RuntimeEngine runtimeEngine = getRuntimeEngine(); // get access to KieSession instance
KieSession ksession = runtimeEngine.getKieSession(); // start process
ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello"); // check whether the process instance has completed successfully
assertProcessInstanceCompleted(processInstance.getId(), ksession); // check what nodes have been triggered
assertNodeTriggered(processInstance.getId(), "StartProcess", "Hello", "EndProcess");
}
} JbpmJUnitBaseTestCase作为基本测试用例类,应当用于jBPM相关测试。它提供了四个使用领域:
JUnit生命周期方法
设置:执行@Before并配置数据源,会清理单例的会话id
拆卸:执行@After和清除历史,关闭会和数据源,处分RuntimeEngines RuntimeManager
知识库和KnowledgeSession管理方法
createRuntimeManager创建RuntimeManager对给定的一组资产和选择策略
disposeRuntimeManager处分RuntimeManager目前活跃在测试的范围
getRuntimeEngine创造了新的RuntimeEngine给定的上下文
断言
assertProcessInstanceCompleted
assertProcessInstanceAborted
assertProcessInstanceActive
assertNodeActive
assertNodeTriggered
assertProcessVarExists
assertNodeExists
assertVersionEquals
assertProcessNameEquals
辅助方法
getDs——返回当前配置数据源
getEmf——会返回当前配置
getTestWorkItemHandler -返回测试工作项可能注册的处理程序除了默认注册是什么
clearHistory——清除历史记录
setupPoolingDataSource——设置数据源
JbpmJUnitBaseTestCase支持所有三个预定义RuntimeManager策略作为单元测试的一部分。这足以应使用指定哪个策略时创建运行时管理器作为单独的一部分测试:
public class ProcessHumanTaskTest extends JbpmJUnitBaseTestCase {
private static final Logger logger = LoggerFactory.getLogger(ProcessHumanTaskTest.class); public ProcessHumanTaskTest() {
super(true, false);
}
@Test
public void testProcessProcessInstanceStrategy() {
RuntimeManager manager = createRuntimeManager(Strategy.PROCESS_INSTANCE, "manager", "humantask.bpmn");
RuntimeEngine runtimeEngine = getRuntimeEngine(ProcessInstanceIdContext.get());
KieSession ksession = runtimeEngine.getKieSession();
TaskService taskService = runtimeEngine.getTaskService();
int ksessionID = ksession.getId();
ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello"); assertProcessInstanceActive(processInstance.getId(), ksession);
assertNodeTriggered(processInstance.getId(), "Start", "Task 1");
manager.disposeRuntimeEngine(runtimeEngine);
runtimeEngine = getRuntimeEngine(ProcessInstanceIdContext.get(processInstance.getId()));
ksession = runtimeEngine.getKieSession();
taskService = runtimeEngine.getTaskService();
assertEquals(ksessionID, ksession.getId());
// let john execute Task 1
List<TaskSummary> list = taskService.getTasksAssignedAsPotentialOwner("john", "en-UK");
TaskSummary task = list.get(0);
logger.info("John is executing task {}", task.getName());
taskService.start(task.getId(), "john");
taskService.complete(task.getId(), "john", null); assertNodeTriggered(processInstance.getId(), "Task 2");
// let mary execute Task 2
list = taskService.getTasksAssignedAsPotentialOwner("mary", "en-UK");
task = list.get(0);
logger.info("Mary is executing task {}", task.getName());
taskService.start(task.getId(), "mary");
taskService.complete(task.getId(), "mary", null); assertNodeTriggered(processInstance.getId(), "End");
assertProcessInstanceCompleted(processInstance.getId(), ksession);
}
} 以上是更完整的示例,它使用PerProcessInstance运行时管理策略和使用任务服务来处理用户的任务。
6.8.1.1. Testing integration with external services
现实生活中的业务流程通常包括调用外部服务(例如一个人工任务服务、电子邮件服务器或您自己的特定于域的服务)。我们的特定于域的过程方法的优点之一是,您可以指定自己如何实际执行自己的特定于域的节点,通过注册一个处理程序。和这个处理程序可以根据上下文不同,允许您使用测试处理器单元测试你的过程。当你的单元测试业务流程,您可以注册测试处理程序,然后验证请求特定的服务是否正确,并提供测试响应的服务。例如,假设您有一个电子邮件节点或人工任务作为流程的一部分。单元测试时,你不想发送一个实际的电子邮件,而是测试要求的电子邮件是否包含正确的信息(例如电子邮件,一个个性化的身体,等等)。
默认情况下提供了一个TestWorkItemHandler登记收集所有工作项(工作项代表一个单位的工作,例如发送一个特定的电子邮件或调用一个特定的服务,包含所有数据相关的任务)对于一个给定的类型。这个测试在单元测试来检查处理程序可以查询具体工作是否实际上是要求的执行过程中,与工作相关的数据是正确的。
下面的例子描述了一个过程,发送电子邮件可以测试。尤其是这个测试用例,测试是否会抛出一个异常时,不能发送电子邮件(由通知模拟引擎,无法完成发送电子邮件)。测试用例使用一个测试处理程序简单地注册电子邮件请求时(和允许您测试的数据与电子邮件从,,等等)。一旦引擎无法发送通知电子邮件(使用abortWorkItem(. .)),单元测试验证流程处理这种情况下成功地通过日志和生成一个错误,在这种情况下中止流程实例。
public void testProcess2() { // create runtime manager with single process - hello.bpmn
createRuntimeManager("sample-process.bpmn");
// take RuntimeManager to work with process engine
RuntimeEngine runtimeEngine = getRuntimeEngine(); // get access to KieSession instance
KieSession ksession = runtimeEngine.getKieSession(); // register a test handler for "Email"
TestWorkItemHandler testHandler = getTestWorkItemHandler(); ksession.getWorkItemManager().registerWorkItemHandler("Email", testHandler); // start the process
ProcessInstance processInstance = ksession.startProcess("com.sample.bpmn.hello2"); assertProcessInstanceActive(processInstance.getId(), ksession);
assertNodeTriggered(processInstance.getId(), "StartProcess", "Email"); // check whether the email has been requested
WorkItem workItem = testHandler.getWorkItem();
assertNotNull(workItem);
assertEquals("Email", workItem.getName());
assertEquals("me@mail.com", workItem.getParameter("From"));
assertEquals("you@mail.com", workItem.getParameter("To")); // notify the engine the email has been sent
ksession.getWorkItemManager().abortWorkItem(workItem.getId());
assertProcessInstanceAborted(processInstance.getId(), ksession);
assertNodeTriggered(processInstance.getId(), "Gateway", "Failed", "Error"); } 6.8.1.2。配置的持久性
您可以配置是否需要执行JUnit测试使用的持久性。JUnit测试将使用持久性,在默认情况下,这意味着所有流程实例的状态将被存储在内存中(H2)数据库(启动JUnit测试中设置)和历史日志将被用来检查断言执行历史有关。当不使用持久性,流程实例只会生活在内存和内存中的记录器用于历史断言。
持久性(和设置数据源)由超级构造函数和控制允许后
默认情况下,没有参数的构造函数,最简单的测试用例配置(不初始化数据源和不配置会话持久性)——这通常是用于在内存中流程管理,没有人工任务交互
超级(布尔、布尔)——允许显式地配置持久性和数据源。这是最常见的方式引导jBPM的测试用例
超级(真、假)——在内存中执行与过程管理持久性与人工任务
超级(真的,真的)——执行持久性与持续的流程管理人工任务
超级(布尔、布尔值、字符串)一样——超级(布尔、布尔值),但允许使用另一个持久性单元名称不是默认(org.jbpm.persistence.jpa)
public class ProcessHumanTaskTest extends JbpmJUnitBaseTestCase {
private static final Logger logger = LoggerFactory.getLogger(ProcessHumanTaskTest.class); public ProcessHumanTaskTest() {
// configure this tests to not use persistence for process engine but still use it for human tasks
super(true, false);
}
}
随机推荐
- genymotion 模拟器 真是牛叉了 速度超快啊!!! 不解释了!建议大家速度去体验一把吧!
已经有人写了blog了 我就不再赘述了,详情去这里看去吧!! android genymotion模拟器怎么使用以及和google提供的模拟器性能对比 http://blog.csdn.net/ ...
- uva 10003 Cutting Sticks (区间dp)
本文出自 http://blog.csdn.net/shuangde800 题目链接: 打开 题目大意 一根长为l的木棍,上面有n个"切点",每个点的位置为c[i] 要按照一 ...
- 微信获取用户数据后台写法,author2.0认证
/* 微信授权接口 */ //1.设置路由 router.get('/wechat/userinfo', function(req, res) { var cb = req.query.cb; //设 ...
- 加入收藏夹的js代码(求兼容chrome浏览器的代码)
从网上找了加入收藏夹的js代码,但不兼容chrome,不知道有没有兼容chrome的相关代码,希望有知道的告诉一下,谢谢! 代码如下 $("#id").click(function ...
- android样式布局--->ListView(附上源代码)
在android应用开发过程中,Listview 是经常使用的数据展现控件,往往用于显示列表形式的数据. 假设只显示数据往往会显得非常单调.非常多时候依据须要定义不同的item 背景选项.比如定义数据 ...
- Eclipse无法识别(手机)设备的解决方案
遇到问题 开始学习android一个多月了,用Eclipse开发,用android手机调试.之前一直好好的,突然Eclipse无法识别手机设备了.纠结了好久,找了各种解决方法,弄了一晚上终于解决问题了 ...
- C++面试题一大波
//打印1到最大的n位数. //题目:输入数字n.按顺序打印出从1到最大的n位十进制数.比方: //输入3.则打印出1.2.3一直到最大的3位数999. //[陷阱]:这个题目非常easy想到的办法就 ...
- kettle中调用java类
kettle中调用java类 有时须要在kettle调用java类,如:验证.查询或自己定义加密等.有时甚至连主要的数据訪问都不那么简单,如获取一个存储文件或使用一个数据库连接,某些数据源可能封装在应 ...
- jquery 点击按钮实现listbox的显示与隐藏,点击其他地方按钮外的地方,隐藏listbox
本来不知道如何获取服务器的控件的,这下知道可以这么做了,所以记录下来.... <asp:ImageButton ID="alltime" ImageUrl="ima ...
- English - every和each的用法和区别
两者都有“每个”的意思,但用法不同: (1)each具有名词和形容词的功能,every只有形容词的功能. (2)each指两个或两个以上的人或事物中的“每个”:every是指三个以上的人或事物的“全体 ...