Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Ode Wiki" for change 
notification.

The following page has been changed by MatthieuRiou:
http://wiki.apache.org/ode/Jacob

The comment on the change is:
Re-ordering (again) the order of paragraphs.

------------------------------------------------------------------------------
  
  By rolling up these concerns in the framework, the implementation of the BPEL 
constructs can be simpler by limiting itself to implementing the BPEL logic and 
not the infrastructure necessary to support it.
  
- The approach we'll take in this tutorial is looking at examples first, then 
we'll explain the different concepts and the rational behind those. But feel 
free to jump to the [#rational] or [#concepts] sections directly if you rather 
read the other way around.
+ The approach we'll take in this tutorial is looking at the [#rational] of 
Jacob and its [#concepts] first. Then we'll illustrate with one complete 
[#examples example] (more coming...). But if you're a reverse reader, you can 
also decide to start with the [#example1 example].
  
+ [[Anchor(rational)]]
+ = Rationale behind the model =
+ 
+ Let's start from the most classical example of all:
+ 
+ {{{#!java
+ void process(order) {
+   billingService.bill(order.billing);
+   shippingService.ship(order.product, order.shipping, self);
+   shipping = receive("shipping")
+   order.customer.send(shipping.details);
+ }
+ }}}
+ 
+ It's Java like pseudo code that works extremely well, unless:
+ 
+   1. You fail somewhere in the middle and the customer has to order a second 
time.
+   1. Your have too many waiting threads and the VM crashes.
+ 
+ So you change it to:
+ 
+ {{{#!java
+ void process(order) {
+   billingService.bill(order.billing);
+   shippingService.ship(order.product, order.shipping, self);
+   listenOn("shipping", part2);
+ }
+ }}}
+ 
+ {{{#!java
+ void part2(shipping) {
+   order.customer.send(shipping.details);
+ }
+ }}}
+ 
+ That's almost better, but still has a lot of points of failure, where you're 
not sure if you actually billed the customer and shipped the product or not. So:
+ 
+ {{{#!java
+ void process(order) {
+   billingService.bill(order.billing);
+   continue("part2");
+ }
+ 
+ void part2() {
+   shippingService.ship(order.product, order.shipping, self);
+   listenOn("shipping", part2);
+ }
+ 
+ void part2(shipping) {
+   order.customer.send(shipping.details);
+ }  
+ }}}
+ 
+ You're just fracturing the code with two primitives: continue which lets you 
persist state and go to the next one, and listenOn which lets you persist state 
and wait for an external event.
+ 
+ Fracturing the code also addresses concurrency of execution even if you only 
have one thread. For example you could have a large number of 'process' calls 
to bill and ship an order. As we've broken down the whole treatment into 
several small parts, we can control when these separate parts actually get 
called and executed. And we're free to order them.
+ 
+ Let's say we have a small process doing in a sequence:
+ {{{
+ 1. Invoke
+ 2. Receive
+ 3. Wait
+ 4. Invoke
+ }}}
+ If we have 2 parrallel executions of this process and implement it in a 
simple way we'd have:
+ {{{
+ 1. Invoke1
+ 2. Receive1
+ 3. Wait1
+ 4. Invoke1
+ 5. Invoke2
+ 6. Receive2
+ 7. Wait2
+ 8. Invoke2
+ }}}
+ However if we break down the code as shown above by introducing a "middle 
man" (or stack) and do not allow activities to directly call each other (or do 
not have the activity directly calling the sequence that directly calls the 
next activity) we could well obtain the following:
+ {{{
+ 1. Invoke1
+ 5. Invoke2
+ 2. Receive1
+ 3. Wait1
+ 6. Receive2
+ 7. Wait2
+ 4. Invoke1
+ 8. Invoke2
+ }}}
+ From a client standpoint, we've achieved concurrency of execution even with 
one thread.
+ 
+ Next step is adding links, fault handling/termination, compensation and event 
handlers and seeing that continue/listenOn is all you need. The last step is 
just adding implementation details.
+ 
+ = Jacob Example =
+ 
+ Consider the issue of persistence. Imagine a simplified and naive 
implementation of the BPEL constructs <sequence>, <wait>, and <empty>:
+ 
+ {{{#!java
+ class Sequence extends Activity {
+   /** From BPEL Definition */
+   OSequence def;
+   ...
+   void run() {
+      for (OActivity child:def.children)
+        createActivity(child).run();
+   }
+ }
+ 
+ class Wait extends Activity {
+   /** From BPEL Definition */
+   OWait def;
+   ...
+   void run() {
+      Thread.wait(def.duration);
+   }
+ }
+ 
+ class Empty extends Activity {
+   /** From BPEL Definition */
+   OEmpty def;
+   ...
+   void run() {
+      // <empty> activity: do nothing.
+   }
+ }
+ }}}
+ 
+ The above is the "natural" implementation of the constructs in the Java 
language. However, this implementation has some serious and obvious problems: 
the <wait> may specify a duration of days in which case a system failure during 
those days of waiting would mean that the process execution state would be 
lost. It would be nice to replace Thread.wait() in the Wait class with some 
sort of "suspend" operator that would save the execution state to disk.
+ 
+ However there are practical issues to "suspending" to disk. At the point we 
wish to suspend, the call stack looks like:
+ {{{
+ Sequence.run()
+ Wait.run()
+ }}}
+ 
+ In order to save the process to disk, we need to end the current thread of 
control which means popping both stack frames. To do this we have no choice but 
to require the implementation of Wait and Sequence to "cooperate" with this 
requirement thereby greatly complicating the implementation of those 
constructs. This also means that the "natural" implementation model cannot be 
used directly.
+ 
+ JACOB aims to solve this problem by providing an alternate "natural" model 
that allows execution state to be suspended ''without cooperation from the 
implementation classes''. The idea in JACOB is to flatten the call stack and 
rely on explicit communication channels to handle control flow. We now consider 
a simplified JACOB representation of our three BPEL activities:
+ 
+ {{{#!java
+ class Empty  {
+   OEmpty def;
+ 
+   /** channel we use to notify parent we are done. */
+   CompletionChannel myCompletionChannel;
+   ...
+   void run() {
+      // <empty> activity: do nothing...except to
+      // notify our parent that we are done.
+      myCompletionChannel.completed();
+   }
+ }
+ 
+ class Sequence  {
+   OSequence def;
+   CompletionChannel myCompletionChannel;
+   ...
+   void self() {
+     // Start of by instantiating a sequential child runner for the first
+     // (0th) child...
+     instance(new SequenceChildRunner(0))
+   }
+ 
+   class SequenceChildRunner {
+     int currentchild;
+     SequenceChildRunner(int childNumber) { currentchild = childNumber; }
+     void self() {
+        if(currentChild == def.children.size()) {
+          // We are past the last child, the sequence is done.
+          myCompletionChannel.completed();
+        } else {
+          // We still have more children to run..
+   
+          // Create a completion channel for our child.
+          CompletionChannel childCompletionChannel = 
+             newChannel(CompletionChannel.class);
+ 
+          // create a child activity based on the activity type
+          // and parameterized with the model and the completion
+          // channel we just created
+          Activity childActivity = 
+             createActivity(def.children.get(currentChild),
+                            childCompletionChannel);
+ 
+          // instantiate the child activity
+          instance(childActivity);
+ 
+          // create an object to wait for the "completed()" notification
+          // from the child activity.
+          object(new CompletionChannelML(childCompletionChannel)) {
+             void completed() {
+                // Ok, finished with the child, create a runner
+                // to do the next child.
+                instance(new SequenceChildRunner(currentChild+1));
+             }
+          }
+        }
+     }
+   }
+ }
+ 
+ class Wait extends Activity {
+   OWait def;
+   CompletionChannel myCompletionChannel;
+    
+   ...
+   void self() {
+      // Create a channel for an externally managed alarm.
+      TimerChannel timerChannel = newChannel(TimerChannel.class);
+      // register the alarm with the runtime.
+      getRuntimeContext().registerTimer(timerChannel, def.duration);
+ 
+      // create an object to wait for the alarm and then send an
+      // activity completed message to our parent.
+      object(new TimerChannelML(timerChannel) {
+         onTimer() {
+            myCompletionChannel.completed();
+         }
+      });
+    
+   }
+ }
+ }}}
+ 
+ So Jacob constructs help us in breaking the execution stack.
+ 
+ [[Anchor(concepts)]]
+ = Main Jacob Concepts =
+ 
+ == Channels ==
+ 
+ As briefly demonstrated above, channels are interfaces used for communication 
between activities in PXE engine. There are several types of channels like 
!TerminationChannel, !ParentScopeChannel or !CompensationChannel (their 
respective purpose should be obvious from their name). Some basic channels are 
provided to all activities when they're created to allow them to interact with 
their environment. When an activity wants to notifies its parent that it has 
terminated for example, it just calls its parent !TerminationChannel (see the 
Empty example above).
+ 
+ Don't look for channels implementations because there are none. Channels 
implementation is provided through a dynamic proxy (see 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/ChannelFactory.java
 ChannelFactory].createChannel() and 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/ChannelFactory.java
 ChannelFactory].!ChannelInvocationHandler for more). That's one of the levels 
of decoupling between invocation and actual execution in Jacob.
+ 
+ == JavaClosure / Abstraction ==
+ 
+ If you don't care much about the details, the bottom line is: a 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/JavaClosure.java
 JavaClosure] and an 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/Abstraction.java
 Abstraction] are just a method implementation. This metod gets executed when 
the abstraction is executed.
+ 
+ From Wikipedia: "A closure combines the code of a function with a special 
lexical environment bound to that function (scope). Closure lexical variables 
differ from global variables in that they do not occupy the global variable 
namespace. They differ from object oriented object variables in that they are 
bound to functions, not objects.". Normally closures aren't supported in Java 
so 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/JavaClosure.java
 JavaClosure] tries to feel that gap. But it's not a true closure anyway, which 
makes thing easier. Closures in Jacob are statically coded, whereas in most 
languages supporting closures these are dynamic. So basically in Jacob, a 
closure is expected to implement some methods and provides other utility 
methods to manipulate channels and replicate itself.
+ 
+ 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/Abstraction.java
 Abstraction] is just a closure that requires the implementation of only one 
method: self(). As ''all activities inherit from 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/Abstraction.java
 Abstraction]'' they're all supposed to implement their main processing in this 
self() method. Their initialization occur in their respective constructors.
+ 
+ == Method Lists (MLs) ==
+ 
+ ML classes can be seen as the other end of a channel. Only they're not 
invoked directly when one calls a channel method, but only once the Jacob 
engine has popped the channel invocation from its internal stack (again you can 
see how the execution stack gets broken here).
+ 
+ Usually MLs implementations in PXE are inlined because it's just easier to 
declare them in the activities self() method. For example if you look at the 
Sequence example shown above you'll see something like:
+ 
+ {{{#!java
+     void self() {
+       ...
+          // create an object to wait for the "completed()" notification
+          // from the child activity.
+          object(new CompletionChannelML(childCompletionChannel)) {
+             void completed() {
+                // Ok, finished with the child, create a runner
+                // to do the next child.
+                instance(new SequenceChildRunner(currentChild+1));
+             }
+          }
+        }
+     }
+ }}}
+ 
+ The object method here is inherited from !JavaClosure and is just a way to 
hand our ML to Jacob. So that the Jacob runtime can match it with an incoming 
channel message later on.
+ 
+ == VPU and Soup ==
+ 
+ The 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/JacobVPU.java
 VPU] is where all the Jacob processing is occuring. When a !JavaClosure is 
injected inside the VPU, it's actually registered as a 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/soup/Reaction.java
 Reaction], which is just wrapping the closure with the method to call on the 
closure to execute it (in our case always the self() method as we're only 
dealing with Abstraction instances).
+ 
+ The 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/soup/Soup.java
 Soup] (and its implementation 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/FastSoupImpl.java
 FastSoupImpl]) is just a container for all the artifacts managed by the VPU 
(mostly channels and reactions) to organize them in queues where artifacts can 
be pushed and popped. It also records some execution statistics.
+ 
+ So the VPU main processing is just dequeuing a reaction from the soup and 
executing it by calling its abstraction's self() method (remember that the 
reaction just wraps an abstraction). That's all (check 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/JacobVPU.java
 JacobVPU].execute(), you'll see that I'm not lying). However when the 
Abstraction (usually an activity) gets executed the following things can happen:
+ 
+ * if other abstractions (usually other activities) are created, they will be 
appended to the reaction queue,
+ * if new channels are created, they will be saved for later usage,
+ * if channels are invoked, the message will be saved to match against a new 
ML,
+ * if a new ML instance is created, it will be submitted to the VPU that will 
try to match it against a channel invocation.
+ 
+ The VPU is also responsible for persisting its internal state. So when an 
execution stops (for example our process has reach a receive) the VPU state is 
serialized and saved for later reuse. This logic can be seen in 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/bpel-runtime/src/main/java/com/fs/pxe/bpel/runtime/RuntimeContextImpl
 RuntimeContextImpl].execute().
+ 
+ There's one more thing that should be mentioned here. Reactions (and hence 
Abstractions) don't "stay" in the VPU queues. They just get popped, executed 
and that's it. So if an abstraction must last more than one execution, it 
should simply fork itself. This explains why in our Sequence example already 
pasted above we see the line:
+ 
+ {{{#!java
+    instance(new SequenceChildRunner(currentChild+1));
+ }}}
+ 
+ This simple adds a new !ChildRunner that will monitor the next child 
completion. If you browse PXE's activities code you will even find things like 
instance(this) which directly enqueues a new instance of the same Jacob 
abstraction.
+ 
+ [[Anchor(examples)]]
  = Walking through examples =
  
+ [[Anchor(example1)]]
  == While ==
  
  {{{#!xml
@@ -232, +521 @@

  
  Finally, when the while condition becomes false, it notifies its parent 
channel. The sequence then goes to our last activity: reply. As expected, the 
reply replies, just sending the variable content and notifying its parent for 
completion. The sequence has no more children to execute so it also notifies 
its own parent, which is the process. We then just declare the process to be 
completed and that's it! We're done!
  
- = Rationale behind the model =
- [[Anchor(rational)]]
- 
- Let's start from the most classical example of all:
- 
- {{{#!java
- void process(order) {
-   billingService.bill(order.billing);
-   shippingService.ship(order.product, order.shipping, self);
-   shipping = receive("shipping")
-   order.customer.send(shipping.details);
- }
- }}}
- 
- It's Java like pseudo code that works extremely well, unless:
- 
-   1. You fail somewhere in the middle and the customer has to order a second 
time.
-   1. Your have too many waiting threads and the VM crashes.
- 
- So you change it to:
- 
- {{{#!java
- void process(order) {
-   billingService.bill(order.billing);
-   shippingService.ship(order.product, order.shipping, self);
-   listenOn("shipping", part2);
- }
- }}}
- 
- {{{#!java
- void part2(shipping) {
-   order.customer.send(shipping.details);
- }
- }}}
- 
- That's almost better, but still has a lot of points of failure, where you're 
not sure if you actually billed the customer and shipped the product or not. So:
- 
- {{{#!java
- void process(order) {
-   billingService.bill(order.billing);
-   continue("part2");
- }
- 
- void part2() {
-   shippingService.ship(order.product, order.shipping, self);
-   listenOn("shipping", part2);
- }
- 
- void part2(shipping) {
-   order.customer.send(shipping.details);
- }  
- }}}
- 
- You're just fracturing the code with two primitives: continue which lets you 
persist state and go to the next one, and listenOn which lets you persist state 
and wait for an external event.
- 
- Fracturing the code also addresses concurrency of execution even if you only 
have one thread. For example you could have a large number of 'process' calls 
to bill and ship an order. As we've broken down the whole treatment into 
several small parts, we can control when these separate parts actually get 
called and executed. And we're free to order them.
- 
- Let's say we have a small process doing in a sequence:
- {{{
- 1. Invoke
- 2. Receive
- 3. Wait
- 4. Invoke
- }}}
- If we have 2 parrallel executions of this process and implement it in a 
simple way we'd have:
- {{{
- 1. Invoke1
- 2. Receive1
- 3. Wait1
- 4. Invoke1
- 5. Invoke2
- 6. Receive2
- 7. Wait2
- 8. Invoke2
- }}}
- However if we break down the code as shown above by introducing a "middle 
man" (or stack) and do not allow activities to directly call each other (or do 
not have the activity directly calling the sequence that directly calls the 
next activity) we could well obtain the following:
- {{{
- 1. Invoke1
- 5. Invoke2
- 2. Receive1
- 3. Wait1
- 6. Receive2
- 7. Wait2
- 4. Invoke1
- 8. Invoke2
- }}}
- From a client standpoint, we've achieved concurrency of execution even with 
one thread.
- 
- Next step is adding links, fault handling/termination, compensation and event 
handlers and seeing that continue/listenOn is all you need. The last step is 
just adding implementation details.
- 
- = Jacob Example =
- 
- Consider the issue of persistence. Imagine a simplified and naive 
implementation of the BPEL constructs <sequence>, <wait>, and <empty>:
- 
- {{{#!java
- class Sequence extends Activity {
-   /** From BPEL Definition */
-   OSequence def;
-   ...
-   void run() {
-      for (OActivity child:def.children)
-        createActivity(child).run();
-   }
- }
- 
- class Wait extends Activity {
-   /** From BPEL Definition */
-   OWait def;
-   ...
-   void run() {
-      Thread.wait(def.duration);
-   }
- }
- 
- class Empty extends Activity {
-   /** From BPEL Definition */
-   OEmpty def;
-   ...
-   void run() {
-      // <empty> activity: do nothing.
-   }
- }
- }}}
- 
- The above is the "natural" implementation of the constructs in the Java 
language. However, this implementation has some serious and obvious problems: 
the <wait> may specify a duration of days in which case a system failure during 
those days of waiting would mean that the process execution state would be 
lost. It would be nice to replace Thread.wait() in the Wait class with some 
sort of "suspend" operator that would save the execution state to disk.
- 
- However there are practical issues to "suspending" to disk. At the point we 
wish to suspend, the call stack looks like:
- {{{
- Sequence.run()
- Wait.run()
- }}}
- 
- In order to save the process to disk, we need to end the current thread of 
control which means popping both stack frames. To do this we have no choice but 
to require the implementation of Wait and Sequence to "cooperate" with this 
requirement thereby greatly complicating the implementation of those 
constructs. This also means that the "natural" implementation model cannot be 
used directly.
- 
- JACOB aims to solve this problem by providing an alternate "natural" model 
that allows execution state to be suspended ''without cooperation from the 
implementation classes''. The idea in JACOB is to flatten the call stack and 
rely on explicit communication channels to handle control flow. We now consider 
a simplified JACOB representation of our three BPEL activities:
- 
- {{{#!java
- class Empty  {
-   OEmpty def;
- 
-   /** channel we use to notify parent we are done. */
-   CompletionChannel myCompletionChannel;
-   ...
-   void run() {
-      // <empty> activity: do nothing...except to
-      // notify our parent that we are done.
-      myCompletionChannel.completed();
-   }
- }
- 
- class Sequence  {
-   OSequence def;
-   CompletionChannel myCompletionChannel;
-   ...
-   void self() {
-     // Start of by instantiating a sequential child runner for the first
-     // (0th) child...
-     instance(new SequenceChildRunner(0))
-   }
- 
-   class SequenceChildRunner {
-     int currentchild;
-     SequenceChildRunner(int childNumber) { currentchild = childNumber; }
-     void self() {
-        if(currentChild == def.children.size()) {
-          // We are past the last child, the sequence is done.
-          myCompletionChannel.completed();
-        } else {
-          // We still have more children to run..
-   
-          // Create a completion channel for our child.
-          CompletionChannel childCompletionChannel = 
-             newChannel(CompletionChannel.class);
- 
-          // create a child activity based on the activity type
-          // and parameterized with the model and the completion
-          // channel we just created
-          Activity childActivity = 
-             createActivity(def.children.get(currentChild),
-                            childCompletionChannel);
- 
-          // instantiate the child activity
-          instance(childActivity);
- 
-          // create an object to wait for the "completed()" notification
-          // from the child activity.
-          object(new CompletionChannelML(childCompletionChannel)) {
-             void completed() {
-                // Ok, finished with the child, create a runner
-                // to do the next child.
-                instance(new SequenceChildRunner(currentChild+1));
-             }
-          }
-        }
-     }
-   }
- }
- 
- class Wait extends Activity {
-   OWait def;
-   CompletionChannel myCompletionChannel;
-    
-   ...
-   void self() {
-      // Create a channel for an externally managed alarm.
-      TimerChannel timerChannel = newChannel(TimerChannel.class);
-      // register the alarm with the runtime.
-      getRuntimeContext().registerTimer(timerChannel, def.duration);
- 
-      // create an object to wait for the alarm and then send an
-      // activity completed message to our parent.
-      object(new TimerChannelML(timerChannel) {
-         onTimer() {
-            myCompletionChannel.completed();
-         }
-      });
-    
-   }
- }
- }}}
- 
- So Jacob constructs help us in breaking the execution stack.
- 
- = Main Jacob Concepts =
- [[Anchor(concepts)]]
- 
- == Channels ==
- 
- As briefly demonstrated above, channels are interfaces used for communication 
between activities in PXE engine. There are several types of channels like 
!TerminationChannel, !ParentScopeChannel or !CompensationChannel (their 
respective purpose should be obvious from their name). Some basic channels are 
provided to all activities when they're created to allow them to interact with 
their environment. When an activity wants to notifies its parent that it has 
terminated for example, it just calls its parent !TerminationChannel (see the 
Empty example above).
- 
- Don't look for channels implementations because there are none. Channels 
implementation is provided through a dynamic proxy (see 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/ChannelFactory.java
 ChannelFactory].createChannel() and 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/ChannelFactory.java
 ChannelFactory].!ChannelInvocationHandler for more). That's one of the levels 
of decoupling between invocation and actual execution in Jacob.
- 
- == JavaClosure / Abstraction ==
- 
- If you don't care much about the details, the bottom line is: a 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/JavaClosure.java
 JavaClosure] and an 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/Abstraction.java
 Abstraction] are just a method implementation. This metod gets executed when 
the abstraction is executed.
- 
- From Wikipedia: "A closure combines the code of a function with a special 
lexical environment bound to that function (scope). Closure lexical variables 
differ from global variables in that they do not occupy the global variable 
namespace. They differ from object oriented object variables in that they are 
bound to functions, not objects.". Normally closures aren't supported in Java 
so 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/JavaClosure.java
 JavaClosure] tries to feel that gap. But it's not a true closure anyway, which 
makes thing easier. Closures in Jacob are statically coded, whereas in most 
languages supporting closures these are dynamic. So basically in Jacob, a 
closure is expected to implement some methods and provides other utility 
methods to manipulate channels and replicate itself.
- 
- 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/Abstraction.java
 Abstraction] is just a closure that requires the implementation of only one 
method: self(). As ''all activities inherit from 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/Abstraction.java
 Abstraction]'' they're all supposed to implement their main processing in this 
self() method. Their initialization occur in their respective constructors.
- 
- == Method Lists (MLs) ==
- 
- ML classes can be seen as the other end of a channel. Only they're not 
invoked directly when one calls a channel method, but only once the Jacob 
engine has popped the channel invocation from its internal stack (again you can 
see how the execution stack gets broken here).
- 
- Usually MLs implementations in PXE are inlined because it's just easier to 
declare them in the activities self() method. For example if you look at the 
Sequence example shown above you'll see something like:
- 
- {{{#!java
-     void self() {
-       ...
-          // create an object to wait for the "completed()" notification
-          // from the child activity.
-          object(new CompletionChannelML(childCompletionChannel)) {
-             void completed() {
-                // Ok, finished with the child, create a runner
-                // to do the next child.
-                instance(new SequenceChildRunner(currentChild+1));
-             }
-          }
-        }
-     }
- }}}
- 
- The object method here is inherited from !JavaClosure and is just a way to 
hand our ML to Jacob. So that the Jacob runtime can match it with an incoming 
channel message later on.
- 
- == VPU and Soup ==
- 
- The 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/JacobVPU.java
 VPU] is where all the Jacob processing is occuring. When a !JavaClosure is 
injected inside the VPU, it's actually registered as a 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/soup/Reaction.java
 Reaction], which is just wrapping the closure with the method to call on the 
closure to execute it (in our case always the self() method as we're only 
dealing with Abstraction instances).
- 
- The 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/soup/Soup.java
 Soup] (and its implementation 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/FastSoupImpl.java
 FastSoupImpl]) is just a container for all the artifacts managed by the VPU 
(mostly channels and reactions) to organize them in queues where artifacts can 
be pushed and popped. It also records some execution statistics.
- 
- So the VPU main processing is just dequeuing a reaction from the soup and 
executing it by calling its abstraction's self() method (remember that the 
reaction just wraps an abstraction). That's all (check 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/jacob/src/main/java/com/fs/jacob/vpu/JacobVPU.java
 JacobVPU].execute(), you'll see that I'm not lying). However when the 
Abstraction (usually an activity) gets executed the following things can happen:
- 
- * if other abstractions (usually other activities) are created, they will be 
appended to the reaction queue,
- * if new channels are created, they will be saved for later usage,
- * if channels are invoked, the message will be saved to match against a new 
ML,
- * if a new ML instance is created, it will be submitted to the VPU that will 
try to match it against a channel invocation.
- 
- The VPU is also responsible for persisting its internal state. So when an 
execution stops (for example our process has reach a receive) the VPU state is 
serialized and saved for later reuse. This logic can be seen in 
[http://svn.apache.org/repos/asf/incubator/ode/scratch/pxe/bpel-runtime/src/main/java/com/fs/pxe/bpel/runtime/RuntimeContextImpl
 RuntimeContextImpl].execute().
- 
- There's one more thing that should be mentioned here. Reactions (and hence 
Abstractions) don't "stay" in the VPU queues. They just get popped, executed 
and that's it. So if an abstraction must last more than one execution, it 
should simply fork itself. This explains why in our Sequence example already 
pasted above we see the line:
- 
- {{{#!java
-    instance(new SequenceChildRunner(currentChild+1));
- }}}
- 
- This simple adds a new !ChildRunner that will monitor the next child 
completion. If you browse PXE's activities code you will even find things like 
instance(this) which directly enqueues a new instance of the same Jacob 
abstraction.
- 

Reply via email to