Ok, I finally got something working, but I'm afraid it will be
brittle. I figured that using a service and notification events would
be more flexible, produce code with looser coupling with Ruote, and
provide a path toward better asynchronicity in the future. So, I
removed the wait code from my participants and added it to a static
module. I then created a service called "PatientNotifier" like this:

  # Wakes a thread that is stopped using `Patient::wait_for`. It will
wake the
  # thread in the following
circumstances:
  #
  # * when a "dispatch" event is received that has a workitem that
matches the
  #   criteria passed to
`Patient::wait_for`
  # * when a "reply" event is received, and no further events are
received for
  #   up to a `listen_for`
seconds.
  #
  # This latter circumstance is potentially problematic if some
messages take
  # more than `listen_for` seconds to be processed, but it's the only
way to
  # detect situations where a "dispatch" won't be sent until something
else
  # happens, eg. when one branch of one or more concurrent branches
has ended.
  # This is unavoidable given Ruote's design of never knowing what's
going to
  # happen next.
  class PatientNotifier <
ApplicationNotifier
    class << self
      def subscribe_to; %w(reply dispatch);
end
      def listen_for; 1;
end
 
end

    def initialize *args
      super *args
      @lock =
Mutex.new
 
end

    def dispatches; @lock.synchroni...@dispatches}; end
    def dispatches= new_d; @lock.synchroni...@dispatches = new_d};
end
    def listen_thread; @lock.synchroni...@listen_thread}; end
    def listen_thread= new_d; @lock.synchroni...@listen_thread =
new_d}; end

    def notify msg
      send "handle_#{msg["action"]}",
msg
    end

    def handle_reply
msg
      start_listening
msg
    end

    def handle_dispatch msg
      self.dispatches << msg if dispatches
      continue
msg
 
end

    def continue
msg
      wi =
Ruote::Workitem.new(msg["workitem"])
      ::Patient.continue wi
    end

    def start_listening msg
      if listen_thread
 
listen_thread.kill
 
end
      self.listen_thread = Thread.new do
        listen
msg
      end
    end

    def listen msg
      self.dispatches =
[]
      sleep self.class.listen_for
      if self.dispatches.empty?
        continue msg
      end
    ensure
      self.listen_thread, self.dispatches = nil, nil
    end
  end

I then call this on my engine: `add_service 'patient_notifier',
'notifiers', 'Workflow::PatientNotifier'`

The rest of the code is pretty similar to the code I posted before
with some slight refactoring to remove it from Participants and put it
into a singleton Patient module (sans #consume method, of course).

I'm interested to hear your thoughts on this approach.

-- 
you received this message because you are subscribed to the "ruote users" group.
to post : send email to [email protected]
to unsubscribe : send email to [email protected]
more options : http://groups.google.com/group/openwferu-users?hl=en

Reply via email to