Listeners/interceptors registered per node or arc

5 views
Skip to first unread message

Mihail

unread,
Aug 10, 2009, 9:45:40 AM8/10/09
to sarasvati-wf-dev
Hi Paul,

Sarasvati has listeners that can be registered at the processes level
for events like enter node or exit node. If I understand correctly
that means the listener is executed every time any node is entered or
exited.

Do you think it makes sense to add functionality that allows one to
add listenerd for a specific node or even for a specific arc?

Also do you think it makes sense to add some sort of interceptor
support at the node or arc level? For example if I want to send an
email when a node is entered it may make sense to do this as an
interceptor or listener.

In general node or arc level interceptors and listeners would allow to
add functionality to nodes in a declarative fashion.

Thanks,
Mihail

Paul Lorenz

unread,
Aug 10, 2009, 3:32:00 PM8/10/09
to sarasvat...@googlegroups.com
Hi Mihail,
  I think per-node or arc listeners would be nice, but I haven't thought about it enough to figure out if there's a clean way to implement it (minimal performance cost and additional code complexity).

The solution I use for this purpose is to make specific nodes into event listeners. So I have a global listener

  public void notify (final ExecutionEvent event)
  {
    ExecutionListener adapter = event.getNodeToken().getNode().getAdaptor( ExecutionListener.class );
    if ( adapter != null )
    {
      adapter.notify( event );
    }
  }

and in nodes that I care about:

  public <T> T getAdaptor (final Class<T> clazz)
  {
    if ( clazz == ExecutionListener.class )
    {
      return (T)this;
    }
    ...
  }

You could also probably use a similar mechanism to implement standalone interceptors.

cheers,
Paul

Mihail

unread,
Aug 10, 2009, 5:02:07 PM8/10/09
to sarasvati-wf-dev
Paul,

I will work on this. See what I come up with.

In very broad strokes adding listeners per node could be implemented
with these things:
- add a listeners collection to the node. The events that can be
registered are the regular event types you already have for the nodes
- NODE_TOKEN_CREATED, NODE_TOKEN_COMPLETED etc. For the completed
events is also important to specify what arc is taken so probably
event names like NODE_TOKEN_COMPLETED:arcName would do the trick.
- add a table to save the node listeners - for Hibernate configuration
- add an event queue to the node
- add methods to add/remove listeners to a node.
- overwrite executeNode and completeNodeToken in BaseEngine to fire
events to the registered listeners.
- modify the code that builds the Graph from XML - not sure how hard
is to do that. Also change XSD?

In terms of xml configuration this would look something like:
<node name="hello" type="wait">
<listeners>
<listener name="print english greeting"
event="completed:done" class="your.PrintEnglishGreeting.class"/>
<listener name="print frenchgreeting"
event="completed:frenchGreeting"
class="your.PrintFrenchGreeting.class"//>
</listeners>
<arc to="complete" name="done"/>
<arc to="bonjour" name="frenchGreeting"/>
</node>

The idea would be to combine pretty dumb nodes with some generic
building blocks declaratively. Hopefully the end result won't be too
dumb :-)

Do you think this makes sense or you see any obvious problems with
this approach?

I am fairly new to Sarasvati so there is a good chance I may be
missing something.

Mihail

Paul Lorenz

unread,
Aug 11, 2009, 6:03:40 PM8/11/09
to sarasvat...@googlegroups.com
Hi Mihail,
  Sarasvati is at the point now where I don't wish make the core any more complex that it already is, if the purpose can be built on top of existing functionality. This won't work for everything, but lets look at it from this perspective first.

Here is my analysis of what you need:

I. An interface for node listeners.
-------------------------------------------------------------------------------------------------------------------------------------------
ExecutionListener can be reused here. It will only receive node specific events.

II. A way to link listeners to nodes.
-------------------------------------------------------------------------------------------------------------------------------------------
  i. Defining listeners
     You could use <custom>, however that might be problematic. It's generally assumed that the contents of custom will all be loaded together. I think it might be interesting to allow optional modules to Sarasvati. The definition file then allow any number of <module id="moduleId"><!- xs:any --></module> sections.

  ii. Storing links
     There are a couple of options here.
       1. You could store the listeners in wf_node_properties
       2. You could create a sub-type of HibNode, which is tied to a custom table, allowing you store the listener definitions in a more structured format.

III. A way to invoke the interceptors -------------------------------------------------------------------------------------------------------------------------------------------
A global process listener would handle this. It could use the getAdapter method to get an ExecutionQueue from the Node.


As I see it, the main thing that Sarasvati is missing is a way to add custom elements to nodes that are handled by some plugin logic, rather than pushed through the default custom node logic. There may be a way to extend the NodeFactory interface to do this, or perhaps a Module interface would be best. Something like:

interface SarasvatiExtension
{
   void install (Engine engine);
   void load (Node node, Object moduleInfo);
}

and add a method to Engine: addExtenstion( SarasvatiExtension ext );

This would let it register the global process listener as well.

The reason I'm trying to figure out how to do this without touching the core is that I can see many potential enhancements, some of the incompatible. If we can handle them with an extension mechanism, the core stays maintainable while we can still optionally provide the features people want.

Thoughts?

Paul
Message has been deleted

Mihail

unread,
Aug 13, 2009, 2:08:02 PM8/13/09
to sarasvati-wf-dev
Hi Paul,

Oops! Since I clicked the wrong reply link so I am posting this to
the forum. Not enough sleep I guess and blurry vision -)

Thank you for the excellent feedback!

You asked for my thougts so here you go ...

Let's look at a concrete example to put this discussion in a more
precise context.

One of the things we need in our projects is to send email
notifications
during process execution.
There are two types of notifications
- notify a user that the process reached a node that needs his
action
- notify interested parties when the process takes a specific arc.
This means if the node is completed by taking arc A send email A,
if
the node is completed by taking node B send email B

The email needs the following inputs
- runtime data present in the process env attributes
- notification groups - defined at configuration time
- mail subject template - defined at configuration time
- mail body template - defined at configuration time
We the use a template engine - Velocity to put the runtime data into
the email

One approach is to create a custom node to do this but that creates a
hidden contract and limits our options in the future. What I mean by
hidden contract is I have to remember every time that I can add email
notifications to nodes of type X but cannot add to nodes of
type Y. This may not be a big deal but the less things I have to
worry
about the better.

Now let's say that I did create my custom node and later I find that
someone contributes to Sarasvati some other functionality that I need
and is also implemented as a Custom node.
I cannot have multiple inheritance in Java so what are my options?

Ideally the ability to add this sort of functionality should be
transparent
and should not require a custom node IMO. The need for this type of
functionality is not specific to email notification so let's try to
describe
the needs in generic terms.
- need to be able to add custom code for specific nodes at precise
points during process execution
- need to be able to execute the custom code conditionally only if a
specific arc is taken when the node completes
- need to be able to set properties for the custom code during
process definition


What additional data is needed to enable this type of behavior?
- link the nodes to custom code - you already mentioned this in your
reply
- a filter attribute that conditionally executes the code for
specific arcs
- add attributes to the custom code (in my email example specifying
notification groups, email body and subject)


How do we store this information?
Since we need more that a simple way of linking the custom code with
the node, storing this information in wf_node_attr is not going to
work.
At least not in an elegant way. The other option you mentioned was
to create a custom node but I believe this may limit our options in
the future as I mentioned before.

What would seems most natural to me is having separate tables for
this. The problem - this is going to modify the core.

If modifying the core is acceptable we need two extra tables. One
table that links the custom code to the node, specifies the java class
that is invoked, and specifies for what arcs to execute the code.
The second table is used to specify attributes for the custom node.

How do we implement this mechanism?
Two basic choices are
- use listeners and build it on top of the existing listener
functionality
- use interceptors

Although most of the desired functionality can be implemented either
interceptors or listeners, IMO using interceptors for this is more
natural.
For interceptors we could define something like before(), around()
and after() where we can write the custom code. The around() method
allows us to proceed with the node execution or to optionally stop the
execution. This works the same way before, after and around advice
work
with aspects so this is nothing new. Listeners on the other hand get
invoked
when an event is fired and do not offer the option of controlling
node
execution the same way.

Is it worth the trouble?
That is entirely your decission but I think doing this opens up some
interesting possibilities.

First this defines a pretty neat extension mechanism. I am using
interceptors or aspects quite a bit in my projects and I am pretty
happy
with what I can do with them. Maybe this explains my bias.

Some of the functionality that can be implemented this way includes:
- authorization checks per node
- guards - around() method can acts as a guard (I know that is
already a feature).
- email functionality per node
- custom auditing at node level

I implemented some basic interceptor functionality that meets some of
our team's immediate needs and I would be happy to share it with you.
I only wrote the absolute minimum to allow us to make progress on
the field. It has some missing pieces but we can work on that if you
are interested.

Fair warning: I wrote this code before I read your feedback and it
does involve some changes to the core.

Let me know what you think,
Mihail

Cheong Chung Onn

unread,
Aug 13, 2009, 10:09:37 PM8/13/09
to sarasvat...@googlegroups.com
Hi Mihail,

We have similar requirements like yours, the need to send email at the
end of the node and templating for different types of the email
contents. We are able to achieve these with existing Sarasvati APIs though.

My view is such implementations can be varied depending on the choices
of the developer team and it is best not to be part of Sarasvati. There
are also other implications if it is - e.g. What if if the email
delivery fails, how should this failure be handled? What if the
developer prefers to use other templating technology instead of Velocity?

To me, I like Sarasvati because it is light weight and it does exactly
what it is suppose to do.

Regards
chung-onn

Mihail

unread,
Aug 14, 2009, 1:59:00 PM8/14/09
to sarasvati-wf-dev
Hi Chung-onn,

I appreciate your feedback on this.

I like Sarasavati for the same reasons - it is lightweight and easy to
work with. This is the reason why we are using it.
I completely understand that keeping the design clean is more
important that adding a particular feature.

I agree with you that adding email functionality and specifying what
template engine to use should not be part of the
core but this is not what I was trying to suggest.

What I was suggesting is to define a generic interception mechanism
that allows us to execute custom code at precise
execution points for specific nodes. Ideally this mechanism would be
completely transparent and would not require
extending a particular node. This is nothing new and many frameworks
use it: Spring supports this via aspects, groovy
allows method interception under the hood via meta programming,
mocking frameworks like jmock work by intercepting
the method calls.

Having this interceptor support allows us to easily add functionality
like email, authorization checks, custom auditing etc
per specific nodes with minimal effort. The only thing we have to do
is implement a generic interface and specify in your
xml or database what nodes get intercepted.

Based on my limited Sarasvati knowledge there are two built in
mechanisms to customize behavior - extend a node or
use listeners defined globally or at the process level. I did not see
anything that allows to add custom behavior at specific
nodes and at specific execution points transparently. Obviously I
could make these two mechanisms work but that would
not be the cleanest approach IMO.

I hope this clarifies what I was suggesting.

Best regards,
Mihail

chungonn

unread,
Aug 15, 2009, 11:33:20 PM8/15/09
to sarasvati-wf-dev
HI Mihail,

Your illustrations on the flexibilities of various frameworks/language
are designed to serve very specific purpose and so is Sarasvati. I do
like the idea of having Sarasvati to have some sort of Plugin
facilities to allow different Profiles be developed by different
developer. Btw, I suggested to include an Injector facility in
Sarasvati in another thread and hopefully it would be there in future
release - well I actually go my project going without it though :).

Currently the only core developer for Sarasvati is Paul and he has to
do following - Java support, Haskell support, Sarasvati for Hibernate,
DAO and Memory mode, Visual Editor plus lots of documentation update.
Fyi, since RC2 there are really a lot of enhancements put into
Sarasvati... personally I am amazed how Paul manage to do it :)

For Sarasvati, each time when a new feature is to be implemented, Paul
has to ensure the facility/feature is make available to the various
subsystem, it is a tough job and it demands lots of effort and time.

Right now, I think it is best to get a highly robust, functional
version 1.0 out first and put new features and capabilities in
Sarasvati roadmap.

What are your thoughts?

Regards
chung-onn

Paul Lorenz

unread,
Aug 16, 2009, 11:24:00 AM8/16/09
to sarasvat...@googlegroups.com
My main drive right now is to finish up the documentation for RC3 and get it out the door.

I think the plugin system will work for to implement the interceptors, and might be a nice test case. I've been thinking about it, and realized there is no reason the interceptor mappings have to be stored in the nodes.

Take the hibernate case. You can have a intercepters stored in their own entity:

HibNodeInterceptor
{
   Node node;
   EventType type;
   String arcName;
   String className;
  
   public void notify (ExecutionEvent event)
   {
      // creates interceptor instance, invokes if appropriate
   }
}

Your process listener could then look up the interceptors

public void notify ( ... )
{
   Node node = event.getNodeToken().getNode();
   for ( HibNodeInterceptor i : ((HibEngine)event.getEngine()).getSession( "from HibNodeInterceptor where node = :node" ) )
   {
     i.notify( event );
   }
}

...

So I think it'll work. The implementation won't be as tidy as if you put in the core, but it should work pretty much as you intend. As long as the API is satisfactory, I'm not bothered that the implementation is slightly more convoluted, as it allows the core to remain simpler.

The only thing that it can't do is affect the lifecycle. I think if you want interceptors to act as guards, they need to be invoked from the guard. I would be interested in allowing listeners to prvent the node from being executed right away. This would allow an interceptor to implement some of the timing logic that has been under discussion.

I'll hopefully be able to throw together a prototype of the plugin/extension system after the RC3 release (unless someone else wants to take a crack at first).

cheers,
Paul

Paul Lorenz

unread,
Aug 16, 2009, 11:26:23 AM8/16/09
to sarasvat...@googlegroups.com
Hi chung-onn,
  I've been talking with a friend of mine about the IOC stuff, since he uses spring. We thought that it might be possible to use Custom nodes as an IOC point, since they are wrapped by CustomNodeWrappers and are already looked up at runtime. They could be looked up from a NodeFactory which delegates to an IOC container.

Another thing to look at after RC3.

cheers,
Paul

On Sat, Aug 15, 2009 at 11:33 PM, chungonn <chun...@gmail.com> wrote:

Cheong Chung Onn

unread,
Aug 16, 2009, 9:48:56 PM8/16/09
to sarasvat...@googlegroups.com
Hi Paul,

Thanks for pursuing the IOC matter. When IOC becomes available then
hopefully with Guice's AOP facility it would meet Mihail needs :-)

Cheers!
chung-onn

Paul Lorenz

unread,
Aug 16, 2009, 10:06:13 PM8/16/09
to sarasvat...@googlegroups.com
Oh, yeah. I forgot that some of the IOC containers (like Seam) also allow interceptors.

Paul
Reply all
Reply to author
Forward
0 new messages