On 2/2/07, Elam Birnbaum (ebirnbau) <[EMAIL PROTECTED]> wrote:
Hello everyone,

This is my first post concerning Commons SCXML so please feel free to
provide suggestions on the semantics of my post, the question itself, or
the design I have chosen. Apologies in advance for the long e-mail!

<snip/>

Welcome, and I wouldn't worry about those things, if the thoroughness
of this email is anything to go by ;-)


Introduction

What I want to do is to have an SCXML document that at various points
calls out to a Java class to perform internal things. Now I expect these
SCXML documents to be exposed to my customers so the use of
AbstractStateMachine is a bit limiting because of its requirements to
have method names match state names and some additional weird behavior
with attempting to throw events from within those methods.
<snap/>

Correct, that convenience class exists mainly for simple usecases
(such as the stopwatch example).


So I have
chosen to use <invoke> with a custom target type of "java" and my own
Invoker class. This works very well for me because it allows me to have
a single invoker class that can then act as my entrance point to other
code which can perform arrange of different activities, based on which
state it is in.

<snip/>

Agreed.


Another function of my project is that the custom code that is executed
by the invoke sends out messages. I then have registered message handler
classes that are activated when I get a response message, but in
separate threads spawned by a message handling thread pool. It is this
message handler class that will then have to trigger SCXML events that
cause the state machine to progress. In other words, my program has one
thread that sets the state engine going, which enters the invoke and
runs the invoke method of my invoker, but that invoke cannot trigger an
event until I receive an asynchronous message. So the invoke method does
not trigger any events within it and the state machine sits and waits.
Then a separate thread activates the message handler, which then gets
access to the SCXMLExecutor class and triggers an event which causes the
special done event to be triggered for the invoke and the state machine
to continue.
<snap/>

Sounds reasonable.


This actually works fine, but I am seeing some weird
behavior from within the Commons SCXML library that I am not sure
whether is an indication that I am doing something wrong or an issue
within the library.

I will include a small class and the SCXML that I am using as an example
of what I am doing. I will also include the output that I see and cannot
explain.

<snip-much-of-example/>

     public void invoke(final String source, final Map params) throws
InvokerException {
      System.out.println("Source = " + source);
         try {
       if (source.contains("foo")) {
        System.out.println("Invoking Foo. Sending special done event.");
           parentSCInstance.getExecutor().triggerEvent(new
TriggerEvent(parentStateId + ".invoke.done",
TriggerEvent.SIGNAL_EVENT));


<snap/>


  public void parentEvents(TriggerEvent[] events) throws
InvokerException {
   System.out.println("In parentEvents for ID " + parentStateId +
     ". There are " + events.length + " events. " +
     "First event name = " + events[0].getName());
   if (events[0].getName().equals("subsequent") &&
parentStateId.equals("step2")) {
          try {
        System.out.println("Invoking Bar, Part 2. We are now done so we
send the special done event.");
           parentSCInstance.getExecutor().triggerEvent(new
TriggerEvent(parentStateId + ".invoke.done",
TriggerEvent.SIGNAL_EVENT));

<snip/>

This (turning around and triggering an event on the state machine) is
a no-no based on the following:

* Once a state machine receives external stimulus (as one or more
events), it must complete processing before it can reach a stable
state and begin to process others stimuli. As a corollary, developer
responsibility is in not interrupting a state machine while it is
processing external event(s).

* Commons SCXML uses JDK 1.4 and synchronized triggerEvent()
method(s). If the owning thread requests (such as in the above
example), Java will grant it the reentrant lock. Things may seem fine
here, but we've broken the first principle (above bullet).

* <invoke> semantics work best when there is asynchronous
interaction. There shouldn't be any processing (of significance) in
any of the Invoker methods (invoke, parentEvents, cancel), just the
initiation of the processing which will asynchronously continue to
communicate with the state machine as needed.

So, in your test rig, you should change this existing pseudo:

public void invoke(...) {
 delay(); // to simulate processing
 parentSCInstance.getExecutor().triggerEvent(...);
}


to this pseudo:

public void invoke(...) {
 new AsyncMessage(parentSCInstance.getExecutor(), eventName, delay).start();
}


<snip-comments/>

Questions

So after all this, there are some questions I have:

1) Is it correct behavior to have the parentEvents method triggered on
all invokes, even ones for states that have completed? Meaning I should
possibly add a payload to the event to identify which invoke it is meant
for and filter the handling within the parentEvent method? Or maybe is
there a different way to trigger an event on just a single state?

<snip/>

No, there should be no need to add payload.


2) Should the special done event trigger the parentEvents method to be
called on either it or any other invoker instance or should this special
event simply be handled internally as one that signifies that the invoke
is done and to move on?

<snap/>

Both scenarios make sense (you could have other processes listening as
well), its one of things that cannot be decided by the implementation
(we'll see what the next version of the W3C Draft says, or I will
ask).


3) Could it be possible that #1 and #2 are related to a bug in Commons
SCXML that does not properly remove an invoker instance when the state
in which the invoker resides completes? If that was done, then the
situations described above would not happen, right? I have only been
looking at things for a couple of weeks so I don't know if I am missing
something in the design that requires things to be this way and I am
simply not using the library correctly...

<snip/>

I suspect if you make the change above, these will generally work as expected.


4) Why would the cancel method be called in this scenario? Does this
mean that even though the transitions are followed correctly, the
history maintained by the system does not consider that step to have
been completed, or is this just a trigger of a method in the invoker and
just ignoring it causes no issues?

<snap/>

The implementation calls cancel to safeguard against zombie invoked
processes (even though invoke.done was triggered). We could change
that by special casing the invoke.done event as you indicate, I think
I will wait till the next draft (which I understand is due soon) to
make any change.

Could you please create an issue in JIRA [1] about the cancel method
in context of the invoke.done event? That'll ensure we review it
before the next release. Thanks.


Apologies for the long e-mail, but this is a very specific situation
that I needed to provide a lot of introduction in order for my questions
to make sense!

<snip/>

Thanks for all the details, probably saved a few email round trips :-)

Finally, as mentioned on the Commons SCXML home page, the <invoke>
bits are being worked out (in the W3C Working Group, the draft lists a
few things as TBD), so the implementation also will need possible
changes and/or improvements related to the processing of <invoke>
(both semantics and syntax, such as event names) in future releases.

-Rahul

[1] http://jakarta.apache.org/commons/scxml/issue-tracking.html


Thanks,

Elam Birnbaum
Cisco


---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to