|
Page Edited :
ODExSITE :
BPEL Simplified Syntax (simBPEL)
BPEL Simplified Syntax (simBPEL) has been edited by Matthieu Riou (Nov 27, 2007). Change summary: Improved join example to demonstrate joining on several links Proposal by IAASTammo and Oliver from the IAAS Main design goals:
We introduced implicit variable declaration, which does not fulfill the second goal, but perfectly fits to the first goal. BPEL offers block-structured and graph-based programming. Since block-structured programming is more in-line with _javascript_, we first present on a ordering process how this is reflected in BPEL4Coders. The commands are executed in sequential order (BPEL: <sequence>) process OrderCheapestBook {
// partner porttypes - identified by QNames of their WSDL porttype
service amazon = {http://aws.amazon.com/aws}Bookstore
service bol = {http://bws.bol.de/bws}Bookstore
// port types offered by the process. Since the process acts as "client" for the partner, "client" as chosen as keyword
client me = {urn:iaas:toll}BookReseller
// variable declaration. XML support is built-in: po is an XML variable, whose type is defined in a WSDL
var {urn:iaas:ourxsd}PurchaseOrder po// the variable purchaseOrder is declared // the type is the type returned by the function me.orderBook() // me is a client. Therefore the semantics of function calls on that object is to wait for a partner to call. The received message is returned by the function // BPEL: <receive partnerLink="me" operation="orderBook" variable="purchaseOrder" /> var purchaseOrder = me.orderBook() // Service declaration. Type is determined by the first assignment to it service res // Variables without direct type declaration get their type by the first assigment to them var tmp1, tmp2 // Parallel execution (BPEL: <flow> instead of <sequence>.) // nested sequential execution can be made by "seq {...}" par { tmp1 = amazon.getDollarPriceForISBN() tmp2 = bol.getEURPriceForISBN() } // compiler exception tmp1 = tmp2 // TODO: compiler exception, since amazon and bol are type incompatible if (tmp1/@price < tmp2/@price) { res = amazon } else { res = bol } // me.orderBook() is a synchronous operation // <reply> is done by assigning a value to the operation (cf. Pascal-Syntax) me.orderBook() = res.buyBook() } Having seen the block-structured part, a simplified loan approval process is now used to illustrate the graph-based part of the language. process LoanApproval {
service autoAssessor = {urn:auto}pt
service humanAssessor = {urn:human}pt
service customer = {urn:customer}pt
client me = {urn:me}pt
// After the statement, links may come
// Links are separated by commas
// The condition is enclosed in square brackets
// the target of a link (indicated by ->) is a label
var request = me.request() [$request.amount < 50000]->lauto, [$request.amount >= 50000]->lhuman
// Label "lauto" for the autoAssessor
lauto: var risk = autoAssessor.approve(request) l1=[$risk = 'high']->lhuman, l2=[risk = 'low']->lapp
lhuman: var hresult = humanAssessor.approve(request) [$hresult = 'approved']->lapp, [$hresult = 'rejected']->lrej
lapp: customer.appoved();
lrej: customer.rejected();
}
The example misses join conditions. Join conditions are put in square brackets in front of the label. To reference links in the join conditions, links have to be named. This happens by putting <linkName>= in front of the link-condition and target. For example: lauto: var risk = autoAssessor.approve(request) l1=[$risk = 'high']->lhuman, l2=[risk = 'low']->lapp
[l1 and l2] lapp: customer.appoved();
Scopes are enclosed in braces (prefixed by {{scope }} -> Matthieu): // Scope scope { onEvent: me.getStatus() {|msg| // Event handler for call at operation "getStatus()" // msg contains the message sent by the partner client.notify("processing"); } onEvent: me.getExtStatus() {|msg| ... } ... } onFault f { // Fault Handler for fault f } onCompensation { // Compensation Handler ... } onTermination { // Termination Handler ... } ... } There is no disambiguity in the usage of the brackets. For example, if a scope should be nested in a handler, it looks like follows: ...
} onFault f {
scope("test") {
...
} onFault g {
}
} onCompensation {
...
Future work:
Alex/Assaf/Matthieu proposalFirst a few general points:
Optional target namespace declaration. For process elements it defaults to ODE target namespace. namespace foo = "urn:/example.com"
process foo::request {
}
# Or
use namespace foo
process request {
}
Importing documents definitions. After import, elements can be referenced relatively. foo = import "xsd|wsdl|..." foo.portTypes.bar Function definition in _javascript_ that returns an xpath function, plain _javascript_ inside and allows to "hook" other _expression_ languages. Those functions are also used for correlation (see later). function foo(msg) {
xpath("foo/bar");
}
something.other = foo
something.other = xpath('.....')
Process definition process foo {
...
}
Sequence is always easy sequence {
}
Pick syntax, receive is the same thing without the pick wrapper pick {
partner1.receive(o1) { |msg|
# More BPEL code
}
partner2.receive(o2) { |msg|
}
someVar = partner3.receive(o1) (msg)
timeout(val) {
}
}
Flow, each block is a sequence. The signal and join instructions are here to model links, parrallel {
...
signal(link1, "expr")
...
} and {
...
join([link1, link2, link3], "$link1 and $link2")
...
} and {
}
If, else if and else: if (expr) { } else if (expr) { } else { } While: while(i<10) {
i = i + 1
}
Repeat until: do {
i = i+1
} until(i<10)
Sequential foreach and parrallel foreach are two different constructs. Allow break? for(m = 0; m < 10; m = m + 2) {
}
forall(m = 0..10) {
}
Invoke syntax - toParts? fromParts? foo = plink.invoke(operation, msg) Assignment foo = bar foo = bar + " World" foo = [xpath: concat(bar, " World")] Throw, faultVar is optional: throw faultName, faultVar
Wait, support now()+duration _expression_ wait 2m Correlation is achieved by declaring a function that will extract a value from a message and match it against another value. So there's no real correlation set per se, just a set of functions used to extract data from messages. function oidFromOrder(msg) {
....
}
process {
msg = pl.receive(op)
....
pl.receive(op, {oidFromOrder: msg.order.id}) { |msg2|
....
}
}
We've separated the fault handling role of scopes (try...catch) from the eventing role (*Handlers). try { scope { .... } event (pl, op) (var) { } alarm { } compensation { } } catch (Fault f) { } catch { } ExamplesHello world example process Helloworld {
receive(client, hello) (msg) {
msg = msg + " World"
reply msg
}
}
External counter example process ExternalCounter {
receive(my_pl, start_op) (msg_in) {
resp = <root><count>0</count></root>
while(resp < 10) {
invoke(partner_pl, partner_start_op) (msg_in)
resp = receive(partner_pl, partner_reply_op)
}
reply resp
}
}
|
Unsubscribe or edit your notifications preferences
