BPEL Simplified Syntax (simBPEL) has been edited by Matthieu Riou (Nov 27, 2007).

Change summary:

Improved join example to demonstrate joining on several links

(View changes)

Content:

Proposal by IAAS

Tammo and Oliver from the IAAS took your ideas and worked on a syntax for BPEL4Coders. This file is a quick summary of our results.

Main design goals:

  1. Use _javascript_ Syntax as basis
  2. Provide a 1:1 mapping to BPEL and back

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:

  • Check whether all aspects of <assign> are covered by BPEL4Hackers
  • How should the extension activity be modeled?
    {{{XML-Code for the extension}}}
    (Three braces, because some Wiki-Syntax definitions use that for verbatim text)
  • Should activity naming be supported? (could be done via , name="..." put before/after the outgoing links)
  • Provide syntax for all BPEL activities. Especially forEach (parallel vs. non-parallel)
  • Clarify whether a grouping of clients and services is needed. - BPEL's partnerLink concept offers myRole and partnerRole. BPEL4Coders currently offers either myRole or partnerRole (you can choose between "client" and "service").
  • Describe implicit variable declaration
  • Provide a grammar for BPEL4Coders
  • Describe the transformation from BPEL4Coders to WS-BPEL 2.0

Alex/Assaf/Matthieu proposal

First a few general points:

  • Syntax similar to the C++/Java/_javascript_ family.
  • We preferred language consistency over BPEL compatibility, although everything can be added back to BPEL with extensions.
  • _javascript_ code is supported natively anytime an _expression_ can be passed (rvalues) or at some specific places of the process declaration (like before the process {}).
  • Namespaces are optional. When no namespace is provided for the process, a default one is assumed.
  • Service definitions aren't necessary, we're just dealing with partner links.
  • XML is a native type and can directly be included in-line, just like E4X (including inner expressions).
  • Everything is executed in sequence (through an implicit sequence in some elements).
  • Variables assignment (foo = bar) has a by-copy semantic (like in BPEL) and not by-reference (like in most other languages).

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 {
}

Examples

Hello 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
  }
}

Reply via email to