Please disable Adblockers and enable JavaScript for domain CEWebS.cs.univie.ac.at! We have NO ADS, but they may interfere with some of our course material.

Name: lib/weel.rb 
1:
# encoding: utf-8
2:
#
3:
# This file is part of WEEL.
4:
#
5:
# WEEL is free software: you can redistribute it and/or modify it under the terms
6:
# of the GNU General Public License as published by the Free Software Foundation,
7:
# either version 3 of the License, or (at your option) any later version.
8:
#
9:
# WEEL is distributed in the hope that it will be useful, but WITHOUT ANY
10:
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11:
# PARTICULAR PURPOSE.  See the GNU General Public License for more details.
12:
#
13:
# You should have received a copy of the GNU General Public License along with
14:
# WEEL (file COPYING in the main directory).  If not, see
15:
# <http://www.gnu.org/licenses/>.
16:
 
17:
require 'thread'
18:
 
19:
# OMG!111! deep cloning for ReadHashes
20:
class Object # {{{
21:
  def deep_clone
22:
    return @deep_cloning_obj if @deep_cloning
23:
    @deep_cloning_obj = clone
24:
    @deep_cloning_obj.instance_variables.each do |var|
25:
      val = @deep_cloning_obj.instance_variable_get(var)
26:
      begin
27:
        @deep_cloning = true
28:
        val = val.deep_clone
29:
      rescue TypeError
30:
        next
31:
      ensure
32:
        @deep_cloning = false
33:
      end
34:
      @deep_cloning_obj.instance_variable_set(var, val)
35:
    end
36:
    deep_cloning_obj = @deep_cloning_obj
37:
    @deep_cloning_obj = nil
38:
    deep_cloning_obj
39:
  end
40:
end #}}}
41:
 
42:
class WEEL
43:
  def initialize(*args)# {{{
44:
    @dslr = DSLRealization.new
45:
    @dslr.__weel_handlerwrapper_args = args
46:
 
47:
    initialize_search if methods.include?(:initialize_search)
48:
    initialize_data if methods.include?(:initialize_data)
49:
    initialize_endpoints if methods.include?(:initialize_endpoints)
50:
    initialize_handlerwrapper if methods.include?(:initialize_handlerwrapper)
51:
    initialize_control if methods.include?(:initialize_control)
52:
  end # }}}
53:
 
54:
  module Signal # {{{
55:
    class Skip < Exception; end
56:
    class SkipManipulate < Exception; end
57:
    class StopSkipManipulate < Exception; end
58:
    class Stop < Exception; end
59:
    class Proceed < Exception; end
60:
    class NoLongerNecessary < Exception; end
61:
    class Again < Exception; end
62:
    class Error < Exception; end
63:
  end # }}}
64:
 
65:
  class ReadStructure # {{{
66:
    def initialize(data,endpoints)
67:
      @__weel_data = data
68:
      @__weel_endpoints = endpoints
69:
      @changed_data = []
70:
      @changed_endpoints = []
71:
    end
72:
 
73:
    def data
74:
      ReadHash.new(@__weel_data)
75:
    end
76:
    def endpoints
77:
      ReadHash.new(@__weel_endpoints)
78:
    end
79:
  end # }}}
80:
  class ManipulateStructure # {{{
81:
    def initialize(data,endpoints,status)
82:
      @__weel_data = data
83:
      @__weel_endpoints = endpoints
84:
      @__weel_status = status
85:
      @changed_status = status.id
86:
      @changed_data = []
87:
      @changed_endpoints = []
88:
    end
89:
 
90:
    attr_reader :changed_data, :changed_endpoints
91:
 
92:
    def original_data
93:
      @__weel_data
94:
    end
95:
 
96:
    def original_endpoints
97:
      @__weel_endpoints
98:
    end
99:
 
100:
    def changed_status
101:
      @changed_status != status.id
102:
    end
103:
 
104:
    def data
105:
      ManipulateHash.new(@__weel_data,@changed_data)
106:
    end
107:
    def endpoints
108:
      ManipulateHash.new(@__weel_endpoints,@changed_endpoints)
109:
    end
110:
    def status
111:
      @__weel_status
112:
    end
113:
  end # }}}
114:
  class ManipulateHash # {{{
115:
    def initialize(values,what)
116:
      @__weel_values = values
117:
      @__weel_what = what
118:
    end
119:
 
120:
    def delete(value)
121:
      if @__weel_values.key?(value)
122:
        @__weel_what << value
123:
        @__weel_values.delete(value)
124:
      end
125:
    end
126:
 
127:
    def clear
128:
      @__weel_what += @__weel_values.keys
129:
      @__weel_values.clear
130:
    end
131:
 
132:
    def method_missing(name,*args)
133:
      if args.empty? && @__weel_values.key?(name)
134:
        @__weel_values[name]
135:
      elsif name.to_s[-1..-1] == "=" && args.length == 1
136:
        temp = name.to_s[0..-2]
137:
        @__weel_what << temp.to_sym
138:
        @__weel_values[temp.to_sym] = args[0]
139:
      elsif name.to_s == "[]=" && args.length == 2
140:
        @__weel_values[args[0]] = args[1]
141:
      elsif name.to_s == "[]" && args.length == 1
142:
        @__weel_values[args[0]]
143:
      else
144:
        nil
145:
      end
146:
    end
147:
  end # }}}
148:
 
149:
  class Status # {{{
150:
    def initialize(id,message)
151:
      @id        = id
152:
      @message   = message
153:
    end
154:
    def update(id,message)
155:
      @id        = id
156:
      @message   = message
157:
    end
158:
    attr_reader :id, :message
159:
  end #}}}
160:
 
161:
  class ReadHash # {{{
162:
    def initialize(values,sim=false)
163:
      @__weel_values = values
164:
      @__weel_sim = sim
165:
    end
166:
 
167:
    def to_json(*args)
168:
      @__weel_values.to_json(*args)
169:
    end
170:
 
171:
    def method_missing(name,*args)
172:
      if args.empty? && @__weel_values.key?(name)
173:
        if @__weel_sim
174:
          "➤#{name}"
175:
        else
176:
          @__weel_values[name]
177:
        end
178:
        #TODO dont let user change stuff e.g. if return value is an array (deep clone and/or deep freeze it?)
179:
      else
180:
        nil
181:
      end
182:
    end
183:
  end # }}}
184:
 
185:
  class HandlerWrapperBase # {{{
186:
    def self::inform_state_change(arguments,newstate); end
187:
    def self::inform_syntax_error(arguments,err,code); end
188:
    def self::inform_handlerwrapper_error(arguments,err); end
189:
    def self::inform_position_change(arguments,ipc); end
190:
 
191:
    def initialize(arguments,endpoint=nil,position=nil,continue=nil); end
192:
 
193:
    def activity_handle(passthrough, parameters); end
194:
 
195:
    def activity_result_value; end
196:
    def activity_result_status; end
197:
 
198:
    def activity_stop; end
199:
    def activity_passthrough_value; end
200:
 
201:
    def activity_no_longer_necessary; end
202:
 
203:
    def inform_activity_done; end
204:
    def inform_activity_manipulate; end
205:
    def inform_activity_failed(err); end
206:
    def inform_manipulate_change(status,changed_data,changed_endpoints,data,endpoints); end
207:
 
208:
    def vote_sync_before(parameters=nil); true; end
209:
    def vote_sync_after; true; end
210:
 
211:
    # type       => activity, loop, parallel, choice
212:
    # nesting    => none, start, end
213:
    # eid        => id's also for control structures
214:
    # parameters => stuff given to the control structure
215:
    def simulate(type,nesting,sequence,parent,parameters={}); end
216:
 
217:
    def callback(result=nil,options={}); end
218:
 
219:
    def test_condition(mr,code); mr.instance_eval(code); end
220:
    def manipulate(mr,code,result=nil,status=nil); mr.instance_eval(code); end
221:
  end  # }}}
222:
 
223:
  class Position # {{{
224:
    attr_reader :position
225:
    attr_accessor :detail, :passthrough
226:
    def initialize(position, detail=:at, passthrough=nil) # :at or :after or :unmark
227:
      @position = position
228:
      @detail = detail
229:
      @passthrough = passthrough
230:
    end
231:
  end # }}}
232:
 
233:
   class Continue # {{{
234:
     def initialize
235:
       @q = Queue.new
236:
       @m = Mutex.new
237:
     end
238:
     def waiting?
239:
       @m.synchronize do
240:
         !@q.empty?
241:
       end
242:
     end
243:
     def continue(*args)
244:
       @q.push(args.length <= 1 ? args[0] : args)
245:
     end
246:
     def clear
247:
      @q.clear
248:
     end
249:
     def wait
250:
       @q.deq
251:
     end
252:
   end #}}}
253:
 
254:
  def self::search(weel_search)# {{{
255:
    define_method :initialize_search do
256:
      self.search weel_search
257:
    end
258:
  end # }}}
259:
  def self::endpoint(new_endpoints)# {{{
260:
    @@__weel_new_endpoints ||= {}
261:
    @@__weel_new_endpoints.merge! new_endpoints
262:
    remove_method :initialize_endpoints if method_defined? :initialize_endpoints
263:
    define_method :initialize_endpoints do
264:
      @@__weel_new_endpoints.each do |name,value|
265:
        @dslr.__weel_endpoints[name.to_s.to_sym] = value
266:
      end
267:
    end
268:
  end # }}}
269:
  def self::data(data_elements)# {{{
270:
    @@__weel_new_data_elements ||= {}
271:
    @@__weel_new_data_elements.merge! data_elements
272:
    define_method :initialize_data do
273:
      @@__weel_new_data_elements.each do |name,value|
274:
        @dslr.__weel_data[name.to_s.to_sym] = value
275:
      end
276:
    end
277:
  end # }}}
278:
  def self::handlerwrapper(aClassname, *args)# {{{
279:
    define_method :initialize_handlerwrapper do
280:
      self.handlerwrapper = aClassname
281:
      self.handlerwrapper_args = args unless args.empty?
282:
    end
283:
  end # }}}
284:
  def self::control(flow, &block)# {{{
285:
    @@__weel_control_block = block
286:
    define_method :initialize_control do
287:
      self.description = @@__weel_control_block
288:
    end
289:
  end #  }}}
290:
  def self::flow # {{{
291:
  end #}}}
292:
 
293:
  class DSLRealization # {{{
294:
    def initialize
295:
      @__weel_search_positions = {}
296:
      @__weel_positions = Array.new
297:
      @__weel_main = nil
298:
      @__weel_data ||= Hash.new
299:
      @__weel_endpoints ||= Hash.new
300:
      @__weel_handlerwrapper = HandlerWrapperBase
301:
      @__weel_handlerwrapper_args = []
302:
      @__weel_state = :ready
303:
      @__weel_status = Status.new(0,"undefined")
304:
      @__weel_sim = -1
305:
    end
306:
    attr_accessor :__weel_search_positions, :__weel_positions, :__weel_main, :__weel_data, :__weel_endpoints, :__weel_handlerwrapper, :__weel_handlerwrapper_args
307:
    attr_reader :__weel_state, :__weel_status
308:
 
309:
    # DSL-Constructs for atomic calls to external services (calls) and pure context manipulations (manipulate).
310:
    # Calls can also manipulate context (after the invoking the external services)
311:
    # position: a unique identifier within the wf-description (may be used by the search to identify a starting point)
312:
    # endpoint: (only with :call) ep of the service
313:
    # parameters: (only with :call) service parameters
314:
    def call(position, endpoint, parameters: {}, finalize: nil, update: nil, &finalizeblk)
315:
      __weel_activity(position,:call,endpoint,parameters,finalize||finalizeblk,update)
316:
    end
317:
    def manipulate(position, script=nil, &scriptblk)
318:
      __weel_activity(position,:manipulate,nil,{},script||scriptblk)
319:
    end
320:
 
321:
    # Parallel DSL-Construct
322:
    # Defines Workflow paths that can be executed parallel.
323:
    # May contain multiple branches (parallel_branch)
324:
    def parallel(type=nil)# {{{
325:
      return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
326:
 
327:
      Thread.current[:branches] = []
328:
      Thread.current[:branch_finished_count] = 0
329:
      Thread.current[:branch_event] = Continue.new
330:
      Thread.current[:mutex] = Mutex.new
331:
 
332:
      hw, pos = __weel_sim_start(:parallel) if __weel_sim
333:
 
334:
      __weel_protect_yield(&Proc.new)
335:
 
336:
      Thread.current[:branch_wait_count] = (type.is_a?(Hash) && type.size == 1 && type[:wait] != nil && (type[:wait].is_a?(Integer) && type[:wait] > 0) ? type[:wait] : Thread.current[:branches].size)
337:
      1.upto Thread.current[:branches].size do
338:
        Thread.current[:branch_event].wait
339:
      end
340:
 
341:
      Thread.current[:branches].each do |thread|
342:
        # decide after executing block in parallel cause for coopis
343:
        # it goes out of search mode while dynamically counting branches
344:
        if Thread.current[:branch_search] == false
345:
          thread[:branch_search] = false
346:
        end
347:
        thread[:start_event].continue
348:
      end
349:
 
350:
      Thread.current[:branch_event].wait
351:
 
352:
      __weel_sim_stop(:parallel,hw,pos) if __weel_sim
353:
 
354:
      unless self.__weel_state == :stopping || self.__weel_state == :stopped
355:
        # first set all to no_longer_neccessary
356:
        Thread.current[:branches].each do |thread|
357:
          if thread.alive?
358:
            thread[:nolongernecessary] = true
359:
            __weel_recursive_continue(thread)
360:
          end
361:
        end
362:
        # wait for all
363:
        Thread.current[:branches].each do |thread|
364:
          __weel_recursive_join(thread)
365:
        end
366:
      end
367:
    end # }}}
368:
 
369:
    # Defines a branch of a parallel-Construct
370:
    def parallel_branch(*vars)# {{{
371:
      return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
372:
      branch_parent = Thread.current
373:
 
374:
      if __weel_sim
375:
        # catch the potential execution in loops inside a parallel
376:
        current_branch_sim_pos = branch_parent[:branch_sim_pos]
377:
      end
378:
 
379:
      Thread.current[:branches] << Thread.new(*vars) do |*local|
380:
        Thread.current.abort_on_exception = true
381:
        Thread.current[:branch_status] = false
382:
        Thread.current[:branch_parent] = branch_parent
383:
        Thread.current[:start_event] = Continue.new
384:
 
385:
        if __weel_sim
386:
          Thread.current[:branch_sim_pos] = @__weel_sim += 1
387:
        end
388:
 
389:
        # parallel_branch could be possibly around an alternative. Thus thread has to inherit the alternative_executed
390:
        # after branching, update it in the parent (TODO)
391:
        if branch_parent[:alternative_executed] && branch_parent[:alternative_executed].length > 0
392:
          Thread.current[:alternative_executed] = [branch_parent[:alternative_executed].last]
393:
          Thread.current[:alternative_mode] = [branch_parent[:alternative_mode].last]
394:
        end
395:
        branch_parent[:branch_event].continue
396:
        Thread.current[:start_event].wait
397:
 
398:
        if __weel_sim
399:
          handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
400:
          handlerwrapper.simulate(:parallel_branch,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos)
401:
        end
402:
 
403:
        __weel_protect_yield(*local, &Proc.new)
404:
 
405:
        __weel_sim_stop(:parallel_branch,handlerwrapper,current_branch_sim_pos) if __weel_sim
406:
 
407:
        branch_parent[:mutex].synchronize do
408:
          Thread.current[:branch_status] = true
409:
          branch_parent[:branch_finished_count] += 1
410:
          if branch_parent[:branch_finished_count] == branch_parent[:branch_wait_count] && self.__weel_state != :stopping
411:
            branch_parent[:branch_event].continue
412:
          end
413:
        end
414:
        if self.__weel_state != :stopping && self.__weel_state != :stopped
415:
          if Thread.current[:branch_position]
416:
            @__weel_positions.delete Thread.current[:branch_position]
417:
            begin
418:
              ipc = {}
419:
              ipc[:unmark] = [Thread.current[:branch_position].position]
420:
              @__weel_handlerwrapper::inform_position_change(@__weel_handlerwrapper_args,ipc)
421:
            end rescue nil
422:
            Thread.current[:branch_position] = nil
423:
          end
424:
        end
425:
      end
426:
    end # }}}
427:
 
428:
    # Choose DSL-Construct
429:
    # Defines a choice in the Workflow path.
430:
    # May contain multiple execution alternatives
431:
    def choose(mode=:inclusive) # {{{
432:
      return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
433:
      Thread.current[:alternative_executed] ||= []
434:
      Thread.current[:alternative_mode] ||= []
435:
      Thread.current[:alternative_executed] << false
436:
      Thread.current[:alternative_mode] << mode
437:
      hw, pos = __weel_sim_start(:choose,:mode => Thread.current[:alternative_mode].last) if __weel_sim
438:
      __weel_protect_yield(&Proc.new)
439:
      __weel_sim_stop(:choose,hw,pos,:mode => Thread.current[:alternative_mode].last) if __weel_sim
440:
      Thread.current[:alternative_executed].pop
441:
      Thread.current[:alternative_mode].pop
442:
      nil
443:
    end # }}}
444:
 
445:
    # Defines a possible choice of a choose-Construct
446:
    # Block is executed if condition == true or
447:
    # searchmode is active (to find the starting position)
448:
    def alternative(condition,args={})# {{{
449:
      return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
450:
      hw, pos = __weel_sim_start(:alternative,args.merge(:mode => Thread.current[:alternative_mode].last, :condition => ((condition.is_a?(String) || condition.is_a?(Proc)) ? condition : nil))) if __weel_sim
451:
      Thread.current[:mutex] ||= Mutex.new
452:
      Thread.current[:mutex].synchronize do
453:
        return if Thread.current[:alternative_mode][-1] == :exclusive && Thread.current[:alternative_executed][-1] == true
454:
        if (condition.is_a?(String) || condition.is_a?(Proc)) && !__weel_sim
455:
          condition = __weel_eval_condition(condition)
456:
        end
457:
        Thread.current[:alternative_executed][-1] = true if condition
458:
      end
459:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || condition
460:
      __weel_sim_stop(:alternative,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last, :condition => ((condition.is_a?(String) || condition.is_a?(Proc)) ? condition : nil))) if __weel_sim
461:
    end # }}}
462:
    def otherwise(args={}) # {{{
463:
      return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
464:
      hw, pos = __weel_sim_start(:otherwise,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
465:
      __weel_protect_yield(&Proc.new) if __weel_is_in_search_mode || __weel_sim || !Thread.current[:alternative_executed].last
466:
      __weel_sim_stop(:otherwise,hw,pos,args.merge(:mode => Thread.current[:alternative_mode].last)) if __weel_sim
467:
    end # }}}
468:
 
469:
    # Defines a critical block (=Mutex)
470:
    def critical(id)# {{{
471:
      @__weel_critical ||= Mutex.new
472:
      semaphore = nil
473:
      @__weel_critical.synchronize do
474:
        @__weel_critical_sections ||= {}
475:
        semaphore = @__weel_critical_sections[id] ? @__weel_critical_sections[id] : Mutex.new
476:
        @__weel_critical_sections[id] = semaphore if id
477:
      end
478:
      semaphore.synchronize do
479:
        __weel_protect_yield(&Proc.new)
480:
      end
481:
    end # }}}
482:
 
483:
    # Defines a Cycle (loop/iteration)
484:
    def loop(condition,args={})# {{{
485:
      unless condition.is_a?(Array) && (condition[0].is_a?(Proc) || condition[0].is_a?(String)) && [:pre_test,:post_test].include?(condition[1]) && args.is_a?(Hash)
486:
        raise "condition must be called pre_test{} or post_test{}"
487:
      end
488:
      return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
489:
      if __weel_is_in_search_mode
490:
        catch :escape do
491:
          __weel_protect_yield(&Proc.new)
492:
        end
493:
        return if __weel_is_in_search_mode
494:
      end
495:
      if __weel_sim
496:
        cond = condition[0].is_a?(Proc) ? true : condition[0]
497:
        hw, pos = __weel_sim_start(:loop,args.merge(:testing=>condition[1],:condition=>cond))
498:
        catch :escape do
499:
          __weel_protect_yield(&Proc.new)
500:
        end
501:
        __weel_sim_stop(:loop,hw,pos,args.merge(:testing=>condition[1],:condition=>cond))
502:
        return
503:
      end
504:
      catch :escape do
505:
        case condition[1]
506:
          when :pre_test
507:
            while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped
508:
              __weel_protect_yield(&Proc.new)
509:
            end
510:
          when :post_test
511:
            begin
512:
              __weel_protect_yield(&Proc.new)
513:
            end while __weel_eval_condition(condition[0]) && self.__weel_state != :stopping && self.__weel_state != :stopped
514:
        end
515:
      end
516:
    end # }}}
517:
 
518:
    def test(code=nil,&blk)# {{{
519:
      code || blk
520:
    end # }}}
521:
    def pre_test(code=nil,&blk)# {{{
522:
      [code || blk, :pre_test]
523:
    end # }}}
524:
    def post_test(code=nil,&blk)# {{{
525:
      [code || blk, :post_test]
526:
    end # }}}
527:
 
528:
    def escape; throw :escape; end
529:
 
530:
    def status # {{{
531:
      @__weel_status
532:
    end # }}}
533:
    def data # {{{
534:
      ReadHash.new(@__weel_data,__weel_sim)
535:
    end # }}}
536:
    def endpoints # {{{
537:
      ReadHash.new(@__weel_endpoints)
538:
    end # }}}
539:
 
540:
  private
541:
    def __weel_protect_yield(*local)
542:
      begin
543:
        yield(*local) if block_given?
544:
      rescue NameError => err # don't look into it, or it will explode
545:
        self.__weel_state = :stopping
546:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("protect_yield: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),nil)
547:
        nil
548:
      rescue => err
549:
        self.__weel_state = :stopping
550:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
551:
        nil
552:
      end
553:
    end
554:
 
555:
    def __weel_eval_condition(condition)
556:
      begin
557:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args unless condition.is_a?(Proc)
558:
        condition.is_a?(Proc) ? condition.call : handlerwrapper.test_condition(ReadStructure.new(@__weel_data,@__weel_endpoints),condition)
559:
      rescue NameError => err # don't look into it, or it will explode
560:
        # if you access $! here, BOOOM
561:
        self.__weel_state = :stopping
562:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("eval_condition: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),nil)
563:
        nil
564:
      rescue => err
565:
        self.__weel_state = :stopping
566:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new(err.message),nil)
567:
        nil
568:
      end
569:
    end
570:
 
571:
    def __weel_activity(position, type, endpoints, parameters, finalize, update=nil)# {{{
572:
      position = __weel_position_test position
573:
      begin
574:
        searchmode = __weel_is_in_search_mode(position)
575:
        return if searchmode == true
576:
        return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
577:
 
578:
        Thread.current[:continue] = Continue.new
579:
        handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args, endpoints.is_a?(Array) ? endpoints.map{ |ep| @__weel_endpoints[ep] }.compact : @__weel_endpoints[endpoints], position, Thread.current[:continue]
580:
 
581:
        if __weel_sim
582:
          handlerwrapper.simulate(:activity,:none,@__weel_sim += 1,Thread.current[:branch_sim_pos],:position => position,:parameters => parameters,:endpoints => endpoints,:type => type,:finalize => finalize.is_a?(String) ? finalize : nil)
583:
          return
584:
        end
585:
 
586:
        ipc = {}
587:
        if searchmode == :after
588:
          wp = WEEL::Position.new(position, :after, nil)
589:
          ipc[:after] = [wp.position]
590:
        else
591:
          if Thread.current[:branch_parent] && Thread.current[:branch_parent][:branch_position]
592:
            @__weel_positions.delete Thread.current[:branch_parent][:branch_position]
593:
            ipc[:unmark] ||= []
594:
            ipc[:unmark] << Thread.current[:branch_parent][:branch_position].position rescue nil
595:
            Thread.current[:branch_parent][:branch_position] = nil
596:
          end
597:
          if Thread.current[:branch_position]
598:
            @__weel_positions.delete Thread.current[:branch_position]
599:
            ipc[:unmark] ||= []
600:
            ipc[:unmark] << Thread.current[:branch_position].position rescue nil
601:
          end
602:
          wp = WEEL::Position.new(position, :at, nil)
603:
          ipc[:at] = [wp.position]
604:
        end
605:
        @__weel_positions << wp
606:
        Thread.current[:branch_position] = wp
607:
 
608:
        @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, ipc
609:
 
610:
        # searchmode position is after, jump directly to vote_sync_after
611:
        raise Signal::Proceed if searchmode == :after
612:
 
613:
        case type
614:
          when :manipulate
615:
            raise Signal::Stop unless handlerwrapper.vote_sync_before
616:
            raise Signal::Skip if self.__weel_state == :stopping
617:
 
618:
            if finalize.is_a?(Proc) || finalize.is_a?(String)
619:
              handlerwrapper.inform_activity_manipulate
620:
              if finalize.is_a?(Proc)
621:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
622:
                mr.instance_eval(&finalize)
623:
              elsif finalize.is_a?(String)
624:
                mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
625:
                handlerwrapper.manipulate(mr,finalize)
626:
              end
627:
              handlerwrapper.inform_manipulate_change(
628:
                ((mr && mr.changed_status) ? @__weel_status : nil),
629:
                ((mr && mr.changed_data.any?) ? mr.changed_data.uniq : nil),
630:
                ((mr && mr.changed_endpoints.any?) ? mr.changed_endpoints.uniq : nil),
631:
                @__weel_data,
632:
                @__weel_endpoints
633:
              )
634:
              handlerwrapper.inform_activity_done
635:
              wp.detail = :after
636:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp.position]
637:
            end
638:
          when :call
639:
            params = { }
640:
            case parameters
641:
              when Hash
642:
                parameters.each do |k,p|
643:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
644:
                    params[k] = @__weel_data[p]
645:
                  else
646:
                    params[k] = p
647:
                  end
648:
                end
649:
              when Array
650:
                parameters.each_with_index do |p,i|
651:
                  if p.is_a?(Symbol) && @__weel_data.include?(p)
652:
                    params[p] = @__weel_data[p]
653:
                  else
654:
                    params[i] = p
655:
                  end
656:
                end
657:
              else
658:
                raise("invalid parameters")
659:
            end
660:
            raise Signal::Stop unless handlerwrapper.vote_sync_before(params)
661:
            raise Signal::Skip if self.__weel_state == :stopping
662:
 
663:
            passthrough = @__weel_search_positions[position] ? @__weel_search_positions[position].passthrough : nil
664:
            handlerwrapper.activity_handle passthrough, params
665:
            begin
666:
              # with loop if catching Signal::Again
667:
              # handshake call and wait until it finished
668:
              waitingresult = nil
669:
              waitingresult = Thread.current[:continue].wait unless Thread.current[:nolongernecessary] || self.__weel_state == :stopping || self.__weel_state == :stopped
670:
              raise waitingresult[1] if !waitingresult.nil? && waitingresult.is_a?(Array) && waitingresult.length == 2 && waitingresult[0] == WEEL::Signal::Error
671:
 
672:
              if Thread.current[:nolongernecessary]
673:
                handlerwrapper.activity_no_longer_necessary
674:
                raise Signal::NoLongerNecessary
675:
              end
676:
              if self.__weel_state == :stopping
677:
                handlerwrapper.activity_stop
678:
                wp.passthrough = handlerwrapper.activity_passthrough_value
679:
                raise Signal::Proceed
680:
              end
681:
 
682:
              code = waitingresult == Signal::Again ? update : finalize
683:
              if code.is_a?(Proc) || code.is_a?(String)
684:
                handlerwrapper.inform_activity_manipulate
685:
                status = handlerwrapper.activity_result_status
686:
                if code.is_a?(Proc)
687:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
688:
                  case code.arity
689:
                    when 1; mr.instance_exec(handlerwrapper.activity_result_value,&code)
690:
                    when 2; mr.instance_exec(handlerwrapper.activity_result_value,(status.is_a?(Status)?status:nil),&code)
691:
                    else
692:
                      mr.instance_exec(&code)
693:
                  end
694:
                elsif code.is_a?(String)
695:
                  mr = ManipulateStructure.new(@__weel_data,@__weel_endpoints,@__weel_status)
696:
                  handlerwrapper.manipulate(mr,code,handlerwrapper.activity_result_value,(status.is_a?(Status)?status:nil))
697:
                end
698:
                handlerwrapper.inform_manipulate_change(
699:
                  (mr.changed_status ? @__weel_status : nil),
700:
                  (mr.changed_data.any? ? mr.changed_data.uniq : nil),
701:
                  (mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil),
702:
                  @__weel_data,
703:
                  @__weel_endpoints
704:
                )
705:
              end
706:
            end while waitingresult == Signal::Again
707:
            if wp.passthrough.nil?
708:
              handlerwrapper.inform_activity_done
709:
              wp.detail = :after
710:
              @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :after => [wp.position]
711:
            end
712:
        end
713:
        raise Signal::Proceed
714:
      rescue Signal::SkipManipulate, Signal::Proceed
715:
        if self.__weel_state != :stopping && !handlerwrapper.vote_sync_after
716:
          self.__weel_state = :stopping
717:
          wp.detail = :unmark
718:
        end
719:
      rescue Signal::NoLongerNecessary
720:
        @__weel_positions.delete wp
721:
        Thread.current[:branch_position] = nil
722:
        wp.detail = :unmark
723:
        @__weel_handlerwrapper::inform_position_change @__weel_handlerwrapper_args, :unmark => [wp.position]
724:
      rescue Signal::StopSkipManipulate, Signal::Stop
725:
        self.__weel_state = :stopping
726:
      rescue Signal::Skip
727:
        nil
728:
      rescue SyntaxError => se
729:
        handlerwrapper.inform_activity_failed se
730:
        self.__weel_state = :stopping
731:
      rescue => err
732:
        handlerwrapper.inform_activity_failed err
733:
        self.__weel_state = :stopping
734:
      ensure
735:
        Thread.current[:continue].clear if Thread.current[:continue] && Thread.current[:continue].is_a?(Continue)
736:
      end
737:
    end # }}}
738:
 
739:
    def __weel_recursive_print(thread,indent='')# {{{
740:
      p "#{indent}#{thread}"
741:
      if thread[:branches]
742:
        thread[:branches].each do |b|
743:
          __weel_recursive_print(b,indent+'  ')
744:
        end
745:
      end
746:
    end  # }}}
747:
    def __weel_recursive_continue(thread)# {{{
748:
      return unless thread
749:
      if thread.alive? && thread[:continue]
750:
        thread[:continue].continue
751:
      end
752:
      if thread.alive? && thread[:branch_event]
753:
        thread[:mutex].synchronize do
754:
          thread[:branch_event].continue unless thread[:branch_event].nil?
755:
        end
756:
      end
757:
      if thread[:branches]
758:
        thread[:branches].each do |b|
759:
          __weel_recursive_continue(b)
760:
        end
761:
      end
762:
    end  # }}}
763:
    def __weel_recursive_join(thread)# {{{
764:
      return unless thread
765:
      if thread.alive? && thread != Thread.current
766:
        thread.join
767:
      end
768:
      if thread[:branches]
769:
        thread[:branches].each do |b|
770:
          __weel_recursive_join(b)
771:
        end
772:
      end
773:
    end  # }}}
774:
 
775:
    def __weel_position_test(position)# {{{
776:
      if position.is_a?(Symbol) && position.to_s =~ /[a-zA-Z][a-zA-Z0-9_]*/
777:
        position
778:
      else
779:
        self.__weel_state = :stopping
780:
        @__weel_handlerwrapper::inform_syntax_error(@__weel_handlerwrapper_args,Exception.new("position (#{position}) not valid"),nil)
781:
      end
782:
    end # }}}
783:
 
784:
    def __weel_is_in_search_mode(position = nil)# {{{
785:
      branch = Thread.current
786:
      return false if @__weel_search_positions.empty? || branch[:branch_search] == false
787:
 
788:
      if position && @__weel_search_positions.include?(position) # matching searchpos => start execution from here
789:
        branch[:branch_search] = false # execute all activities in THIS branch (thread) after this point
790:
        while branch.key?(:branch_parent) # also all parent branches should execute activities after this point, additional branches spawned by parent branches should still be in search mode
791:
          branch = branch[:branch_parent]
792:
          branch[:branch_search] = false
793:
        end
794:
        @__weel_search_positions[position].detail == :after ? :after : false
795:
      else
796:
        branch[:branch_search] = true
797:
      end
798:
    end # }}}
799:
 
800:
    def __weel_sim
801:
      @__weel_state == :simulating
802:
    end
803:
 
804:
    def __weel_sim_start(what,options={})
805:
      current_branch_sim_pos = Thread.current[:branch_sim_pos]
806:
      Thread.current[:branch_sim_pos] = @__weel_sim += 1
807:
      handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
808:
      handlerwrapper.simulate(what,:start,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
809:
      [handlerwrapper, current_branch_sim_pos]
810:
    end
811:
 
812:
    def __weel_sim_stop(what,handlerwrapper,current_branch_sim_pos,options={})
813:
      handlerwrapper.simulate(what,:end,Thread.current[:branch_sim_pos],current_branch_sim_pos,options)
814:
      Thread.current[:branch_sim_pos] = current_branch_sim_pos
815:
    end
816:
 
817:
  public
818:
    def __weel_finalize
819:
      __weel_recursive_join(@__weel_main)
820:
      @__weel_state = :stopped
821:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
822:
    end
823:
 
824:
    def __weel_state=(newState)# {{{
825:
      return @__weel_state if newState == @__weel_state && @__weel_state != :ready
826:
 
827:
      @__weel_positions = Array.new if newState == :running
828:
      @__weel_state = newState
829:
 
830:
      if newState == :stopping
831:
        __weel_recursive_continue(@__weel_main)
832:
      end
833:
 
834:
      @__weel_handlerwrapper::inform_state_change @__weel_handlerwrapper_args, @__weel_state
835:
    end # }}}
836:
 
837:
  end # }}}
838:
 
839:
public
840:
  def positions # {{{
841:
    @dslr.__weel_positions
842:
  end # }}}
843:
 
844:
  # set the handlerwrapper
845:
  def handlerwrapper # {{{
846:
    @dslr.__weel_handlerwrapper
847:
  end # }}}
848:
  def handlerwrapper=(new_weel_handlerwrapper) # {{{
849:
    superclass = new_weel_handlerwrapper
850:
    while superclass
851:
      check_ok = true if superclass == WEEL::HandlerWrapperBase
852:
      superclass = superclass.superclass
853:
    end
854:
    raise "Handlerwrapper is not inherited from HandlerWrapperBase" unless check_ok
855:
    @dslr.__weel_handlerwrapper = new_weel_handlerwrapper
856:
  end # }}}
857:
 
858:
  # Get/Set the handlerwrapper arguments
859:
  def handlerwrapper_args # {{{
860:
    @dslr.__weel_handlerwrapper_args
861:
  end # }}}
862:
  def handlerwrapper_args=(args) # {{{
863:
    if args.class == Array
864:
      @dslr.__weel_handlerwrapper_args = args
865:
    end
866:
    nil
867:
  end #  }}}
868:
 
869:
  # Get the state of execution (ready|running|stopping|stopped|finished|simulating)
870:
  def state # {{{
871:
    @dslr.__weel_state
872:
  end #  }}}
873:
  def state_signal # {{{
874:
    handlerwrapper::inform_state_change handlerwrapper_args, state
875:
    state
876:
  end # }}}
877:
 
878:
  # Set search positions
879:
  # set new_weel_search to a boolean (or anything else) to start the process from beginning (reset serach positions)
880:
  def search(new_weel_search=false) # {{{
881:
    @dslr.__weel_search_positions.clear
882:
 
883:
    new_weel_search = [new_weel_search] if new_weel_search.is_a?(Position)
884:
 
885:
    if !new_weel_search.is_a?(Array) || new_weel_search.empty?
886:
      false
887:
    else
888:
      new_weel_search.each do |search_position|
889:
        @dslr.__weel_search_positions[search_position.position] = search_position
890:
      end
891:
      true
892:
    end
893:
  end # }}}
894:
 
895:
  def data(new_data=nil) # {{{
896:
    unless new_data.nil? || !new_data.is_a?(Hash)
897:
      new_data.each{ |k,v| @dslr.__weel_data[k] = v }
898:
    end
899:
    @dslr.__weel_data
900:
  end # }}}
901:
  def endpoints(new_endpoints=nil) # {{{
902:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash)
903:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
904:
    end
905:
    @dslr.__weel_endpoints
906:
  end # }}}
907:
  def endpoint(new_endpoints) # {{{
908:
    unless new_endpoints.nil? || !new_endpoints.is_a?(Hash) || !new_endpoints.length == 1
909:
      new_endpoints.each{ |k,v| @dslr.__weel_endpoints[k] = v }
910:
    end
911:
    nil
912:
  end # }}}
913:
  def status # {{{
914:
    @dslr.__weel_status
915:
  end # }}}
916:
 
917:
  # get/set workflow description
918:
  def description(&blk)
919:
    self.description=(blk)
920:
  end
921:
  def description=(code) # {{{
922:
    (class << self; self; end).class_eval do
923:
      remove_method :__weel_control_flow if method_defined? :__weel_control_flow
924:
      define_method :__weel_control_flow do |state,final_state=:finished|
925:
        @dslr.__weel_positions.clear
926:
        @dslr.__weel_state = state
927:
        begin
928:
          if code.is_a? Proc
929:
            @dslr.instance_eval(&code)
930:
          else
931:
            @dslr.instance_eval(code)
932:
          end
933:
        rescue SyntaxError => se
934:
          @dslr.__weel_state = :stopping
935:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(se.message),code)
936:
        rescue NameError # => err # don't look into it, or it will explode
937:
          @dslr.__weel_state = :stopping
938:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new("main: `#{err.name}` is not a thing that can be used. Maybe it is meant to be a string and you forgot quotes?"),code)
939:
        rescue => err
940:
          @dslr.__weel_state = :stopping
941:
          @dslr.__weel_handlerwrapper::inform_syntax_error(@dslr.__weel_handlerwrapper_args,Exception.new(err.message),code)
942:
        end
943:
        if @dslr.__weel_state == :running
944:
          @dslr.__weel_state = :finished
945:
          ipc = { :unmark => [] }
946:
          @dslr.__weel_positions.each{ |wp| ipc[:unmark] << wp.position }
947:
          @dslr.__weel_positions.clear
948:
          @dslr.__weel_handlerwrapper::inform_position_change(@dslr.__weel_handlerwrapper_args,ipc)
949:
        end
950:
        if @dslr.__weel_state == :simulating
951:
          @dslr.__weel_state = final_state
952:
        end
953:
        if @dslr.__weel_state == :stopping
954:
          @dslr.__weel_finalize
955:
        end
956:
      end
957:
    end
958:
  end # }}}
959:
 
960:
  # Stop the workflow execution
961:
  def stop # {{{
962:
    Thread.new do
963:
      @dslr.__weel_state = :stopping
964:
      @dslr.__weel_main.join if @dslr.__weel_main
965:
    end
966:
  end # }}}
967:
  # Start the workflow execution
968:
  def start # {{{
969:
    return nil if @dslr.__weel_state != :ready && @dslr.__weel_state != :stopped
970:
    @dslr.__weel_main = Thread.new do
971:
      begin
972:
        __weel_control_flow(:running)
973:
      rescue => e
974:
        puts e.message
975:
        puts e.backtrace
976:
        handlerwrapper::inform_handlerwrapper_error handlerwrapper_args, e
977:
      end
978:
    end
979:
  end # }}}
980:
 
981:
  def sim # {{{
982:
    stat = @dslr.__weel_state
983:
    return nil unless stat == :ready || stat == :stopped
984:
    @dslr.__weel_main = Thread.new do
985:
      __weel_control_flow :simulating, stat
986:
    end
987:
  end # }}}
988:
 
989:
end