openwferu 0.9.4 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. data/examples/mano_tracker.rb +13 -6
  2. data/examples/quotereporter.rb +3 -2
  3. data/lib/openwfe/engine/engine.rb +31 -4
  4. data/lib/openwfe/engine/file_persisted_engine.rb +35 -9
  5. data/lib/openwfe/expool/expressionpool.rb +116 -67
  6. data/lib/openwfe/expool/expstorage.rb +142 -101
  7. data/lib/openwfe/expool/history.rb +7 -2
  8. data/lib/openwfe/expool/yamlexpstorage.rb +150 -6
  9. data/lib/openwfe/expressions/{fe_condition.rb → condition.rb} +4 -6
  10. data/lib/openwfe/expressions/expressionmap.rb +8 -0
  11. data/lib/openwfe/expressions/fe_cancel.rb +109 -0
  12. data/lib/openwfe/expressions/fe_concurrence.rb +252 -16
  13. data/lib/openwfe/expressions/fe_cursor.rb +8 -3
  14. data/lib/openwfe/{util/stoppable.rb → expressions/fe_do.rb} +42 -11
  15. data/lib/openwfe/expressions/fe_iterator.rb +4 -3
  16. data/lib/openwfe/expressions/fe_misc.rb +3 -2
  17. data/lib/openwfe/expressions/fe_participant.rb +5 -0
  18. data/lib/openwfe/expressions/fe_raw.rb +10 -2
  19. data/lib/openwfe/expressions/fe_subprocess.rb +1 -1
  20. data/lib/openwfe/expressions/fe_time.rb +43 -23
  21. data/lib/openwfe/expressions/fe_value.rb +1 -1
  22. data/lib/openwfe/expressions/flowexpression.rb +22 -22
  23. data/lib/openwfe/expressions/raw_prog.rb +20 -39
  24. data/lib/openwfe/expressions/raw_xml.rb +6 -6
  25. data/lib/openwfe/expressions/timeout.rb +8 -3
  26. data/lib/openwfe/expressions/wtemplate.rb +67 -0
  27. data/lib/openwfe/flowexpressionid.rb +4 -1
  28. data/lib/openwfe/participants/atomparticipants.rb +13 -1
  29. data/lib/openwfe/participants/enoparticipant.rb +66 -5
  30. data/lib/openwfe/participants/participantmap.rb +12 -0
  31. data/lib/openwfe/rudefinitions.rb +15 -3
  32. data/lib/openwfe/service.rb +4 -5
  33. data/lib/openwfe/storage/yamlfilestorage.rb +72 -45
  34. data/lib/openwfe/util/dollar.rb +17 -4
  35. data/lib/openwfe/util/lru.rb +154 -0
  36. data/lib/openwfe/util/otime.rb +26 -5
  37. data/lib/openwfe/util/scheduler.rb +44 -36
  38. data/lib/openwfe/util/schedulers.rb +4 -2
  39. data/lib/openwfe/utils.rb +62 -0
  40. data/lib/openwfe/version.rb +1 -1
  41. data/lib/openwfe/worklist/storeparticipant.rb +34 -5
  42. data/test/eno_test.rb +69 -0
  43. data/test/file_persistence_test.rb +13 -11
  44. data/test/flowtestbase.rb +29 -15
  45. data/test/ft_0.rb +2 -1
  46. data/test/ft_0b_sequence.rb +2 -1
  47. data/test/ft_0c_testname.rb +6 -5
  48. data/test/ft_0d_participant.rb +2 -1
  49. data/test/ft_10_loop.rb +11 -6
  50. data/test/ft_10b_loop2.rb +63 -0
  51. data/test/ft_11_ppd.rb +39 -13
  52. data/test/ft_12_blockparticipant.rb +2 -1
  53. data/test/ft_13_eno.rb +3 -2
  54. data/test/ft_14_subprocess.rb +2 -1
  55. data/test/ft_14b_subprocess.rb +2 -1
  56. data/test/ft_15_iterator.rb +2 -1
  57. data/test/ft_16_fqv.rb +2 -1
  58. data/test/ft_17_condition.rb +2 -1
  59. data/test/ft_18_pname.rb +2 -1
  60. data/test/ft_19_csv.rb +2 -1
  61. data/test/ft_1_unset.rb +14 -18
  62. data/test/ft_1b_unset.rb +39 -0
  63. data/test/ft_20_cron.rb +2 -1
  64. data/test/ft_21_cron.rb +2 -1
  65. data/test/ft_22_history.rb +7 -5
  66. data/test/ft_23_when.rb +2 -1
  67. data/test/ft_23b_when.rb +2 -1
  68. data/test/ft_24_def.rb +2 -1
  69. data/test/ft_25_cancel.rb +79 -0
  70. data/test/ft_26_load.rb +197 -0
  71. data/test/ft_2_concurrence.rb +89 -15
  72. data/test/ft_2b_concurrence.rb +152 -0
  73. data/test/ft_2c_concurrence.rb +39 -0
  74. data/test/ft_3_equals.rb +4 -3
  75. data/test/ft_4_misc.rb +4 -3
  76. data/test/ft_5_time.rb +2 -1
  77. data/test/ft_6_lambda.rb +2 -1
  78. data/test/ft_7_lose.rb +53 -17
  79. data/test/ft_8_forget.rb +7 -6
  80. data/test/ft_9_cursor.rb +8 -7
  81. data/test/hparticipant_test.rb +37 -14
  82. data/test/lru_test.rb +79 -0
  83. data/test/misc_test.rb +16 -0
  84. data/test/rake_qtest.rb +7 -0
  85. data/test/raw_prog_test.rb +0 -13
  86. data/test/rutest_utils.rb +15 -2
  87. data/test/scheduler_test.rb +31 -4
  88. data/test/timeout_test.rb +6 -2
  89. data/test/wfid_test.rb +68 -0
  90. metadata +169 -158
  91. data/lib/openwfe/expool/journalexpstorage.rb +0 -312
  92. data/lib/openwfe/util/lru_cache.rb +0 -149
@@ -41,6 +41,7 @@
41
41
 
42
42
  require 'openwfe/utils'
43
43
  require 'openwfe/rudefinitions'
44
+ require 'openwfe/expressions/condition'
44
45
  require 'openwfe/expressions/flowexpression'
45
46
 
46
47
 
@@ -50,7 +51,84 @@ require 'openwfe/expressions/flowexpression'
50
51
 
51
52
  module OpenWFE
52
53
 
54
+ #
55
+ # The concurrence expression will execute each of its (direct) children
56
+ # in parallel threads.
57
+ #
58
+ # Thus,
59
+ #
60
+ # <concurrence>
61
+ # <participant ref="pa" />
62
+ # <participant ref="pb" />
63
+ # </concurrence>
64
+ #
65
+ # Participants pa and pb will be 'treated' in parallel (quasi
66
+ # simultaneously).
67
+ #
68
+ # The concurrence expressions accept many attributes, that can get
69
+ # combined. By default, the concurrence waits for all its children to
70
+ # reply and returns the workitem of the first child that replied.
71
+ # The attributes tune this behaviour.
72
+ #
73
+ # <em>count</em>
74
+ #
75
+ # <concurrence count="1">
76
+ # <participant ref="pa" />
77
+ # <participant ref="pb" />
78
+ # </concurrence>
79
+ #
80
+ # The concurrence will be over as soon as 'pa' or 'pb' replied, i.e.
81
+ # as soon as "1" child replied.
82
+ #
83
+ # <em>remaining</em>
84
+ #
85
+ # The attribute 'remaining' can take two values 'cancel' (the default) and
86
+ # 'forget'.
87
+ # Cancelled children are completely wiped away, forgotten ones continue
88
+ # to operate but their reply will simply get discarded.
89
+ #
90
+ # <em>over-if</em>
91
+ #
92
+ # 'over-if' accepts a 'boolean expression' (something replying 'true' or
93
+ # 'false'), if the expression evaluates to true, the concurrence will be
94
+ # over and the remaining children will get cancelled (the default) or
95
+ # forgotten.
96
+ #
97
+ # <em>merge</em>
98
+ #
99
+ # By default, the first child to reply to its parent 'concurrence'
100
+ # expression 'wins', i.e. its workitem is used for resuming the flow (after
101
+ # the concurrence).
102
+ #
103
+ # [first] The default : the first child to reply wins
104
+ # [last] The last child to reply wins
105
+ # [highest] The first 'defined' child (in the list of children) will win
106
+ # [lowest] The last 'defined' child (in the list of children) will win
107
+ #
108
+ # Thus, in that example
109
+ #
110
+ # <concurrence merge="lowest">
111
+ # <participant ref="pa" />
112
+ # <participant ref="pb" />
113
+ # </concurrence>
114
+ #
115
+ # when the concurrence is done, the workitem of 'pb' is used to resume the
116
+ # flow after the concurrence.
117
+ #
118
+ # <em>merge-type</em>
119
+ #
120
+ # [override] The default : no mix of values between the workitems do occur
121
+ # [mix] Priority is given to the 'winning' workitem but their values
122
+ # get mixed
123
+ #
124
+ # The merge occurs are the top level of workitem attributes.
125
+ #
126
+ # More complex merge behaviour can be obtained by extending the
127
+ # GenericSyncExpression class. But the default sync options are already
128
+ # numerous and powerful.
129
+ #
53
130
  class ConcurrenceExpression < SequenceExpression
131
+ include ConditionMixin
54
132
 
55
133
  attr_accessor \
56
134
  :sync_expression
@@ -73,6 +151,7 @@ module OpenWFE
73
151
  @children.each do |child|
74
152
  Thread.new do
75
153
  begin
154
+ #ldebug { "apply() child : #{child.to_debug_s}" }
76
155
  concurrence.synchronize do
77
156
  get_expression_pool().apply(child, workitem.dup)
78
157
  end
@@ -141,6 +220,11 @@ module OpenWFE
141
220
  @count = determine_count(synchable, workitem)
142
221
  @cancel_remaining = cancel_remaining?(synchable, workitem)
143
222
 
223
+ merge = synchable.lookup_attribute(:merge, workitem, :first)
224
+ merge_type = synchable.lookup_attribute(:merge_type, workitem, :mix)
225
+
226
+ @merge_array = MergeArray.new(merge, merge_type)
227
+
144
228
  @unready_queue = []
145
229
  end
146
230
 
@@ -162,7 +246,11 @@ module OpenWFE
162
246
  synchable.store_itself()
163
247
 
164
248
  queue.each do |workitem|
165
- do_reply(synchable, workitem)
249
+ break if do_reply(synchable, workitem)
250
+ #
251
+ # do_reply() will return 'true' as soon as the
252
+ # concurrence is over, if this is the case, the
253
+ # queue should not be treated anymore
166
254
  end
167
255
  end
168
256
  end
@@ -200,27 +288,52 @@ module OpenWFE
200
288
  "#{workitem.last_expression_id.to_debug_s}"
201
289
  end
202
290
 
291
+ @merge_array.push(synchable, workitem)
292
+
203
293
  @reply_count = @reply_count + 1
204
294
 
205
295
  @remaining_children.delete(workitem.last_expression_id)
206
296
 
207
297
  if @remaining_children.length <= 0
208
- synchable.reply_to_parent(workitem)
209
- return
298
+ reply_to_parent(synchable)
299
+ return true
210
300
  end
211
301
 
212
302
  if @count > 0 and @reply_count >= @count
213
303
  treat_remaining_children(synchable)
214
- synchable.reply_to_parent(workitem)
215
- return
304
+ reply_to_parent(synchable)
305
+ return true
306
+ end
307
+
308
+ #
309
+ # over-if
310
+
311
+ conditional = synchable.eval_condition("over-if", workitem)
312
+
313
+ if conditional
314
+ treat_remaining_children(synchable)
315
+ reply_to_parent(synchable)
316
+ return true
216
317
  end
217
318
 
319
+ #
320
+ # not over, resuming
321
+
218
322
  synchable.store_itself()
219
323
 
220
324
  #synchable.ldebug do
221
325
  # "#{self.class}.do_reply() not replying to parent "+
222
326
  # "#{workitem.last_expression_id.to_debug_s}"
223
327
  #end
328
+
329
+ return false
330
+ end
331
+
332
+ def reply_to_parent (synchable)
333
+
334
+ workitem = @merge_array.do_merge
335
+
336
+ synchable.reply_to_parent(workitem)
224
337
  end
225
338
 
226
339
  def treat_remaining_children (synchable)
@@ -260,20 +373,143 @@ module OpenWFE
260
373
  return -1 if i < 1
261
374
  return i
262
375
  end
263
- end
264
376
 
265
- #
266
- # Merges a workitem (source) into another (target).
267
- # If inPlace is left to false, a brand new workitem is returned,
268
- # else the merge occurs directly into the target workitem.
269
- #
270
- def merge (wiTarget, wiSource, inPlace=false)
377
+ #
378
+ # This inner class is used to gather workitems before the final
379
+ # merge, which is triggered by calling the do_merge() method
380
+ # which returns the merged workitem.
381
+ #
382
+ class MergeArray
271
383
 
272
- wiTarget = wiTarget.dup if not inPlace
384
+ attr_accessor \
385
+ :workitem,
386
+ :workitems_by_arrival,
387
+ :workitems_by_altitude,
388
+ :merge,
389
+ :merge_type
390
+
391
+ def initialize (merge, merge_type)
392
+
393
+ @merge = merge.downcase
394
+ @merge_type = merge_type.downcase
395
+
396
+ ensure_merge_settings()
397
+
398
+ @workitem = nil
399
+
400
+ if highest? or lowest?
401
+ @workitems_by_arrival = []
402
+ @workitems_by_altitude = []
403
+ end
404
+ end
405
+
406
+ def push (synchable, wi)
407
+
408
+ if not @workitems_by_arrival
409
+ #
410
+ # last or first
411
+ #
412
+ source, target = if first?
413
+ [ @workitem, wi ]
414
+ else
415
+ [ wi, @workitem ]
416
+ end
417
+ @workitem = merge(target, source)
418
+
419
+ return
420
+ end
421
+
422
+ index = synchable.children.index(
423
+ wi.last_expression_id)
424
+
425
+ @workitems_by_arrival << wi
426
+ @workitems_by_altitude[index] = wi
427
+ end
428
+
429
+ #
430
+ # merges the workitems stored here
431
+ #
432
+ def do_merge
433
+
434
+ return @workitem if @workitem
435
+
436
+ list = if first?
437
+ @workitems_by_arrival.reverse
438
+ elsif last?
439
+ @workitems_by_arrival
440
+ elsif highest?
441
+ @workitems_by_altitude.reverse
442
+ elsif lowest?
443
+ @workitems_by_altitude
444
+ end
445
+
446
+ result = nil
447
+
448
+ list.each do |wi|
449
+ next unless wi
450
+ result = merge(result, wi)
451
+ end
452
+
453
+ #puts "___ result :"
454
+ #puts result.to_s
455
+ #puts
456
+
457
+ return result
458
+ end
459
+
460
+ protected
461
+
462
+ def first?
463
+ @merge == "first"
464
+ end
465
+ def last?
466
+ @merge == "last"
467
+ end
468
+ def highest?
469
+ @merge == "highest"
470
+ end
471
+ def lowest?
472
+ @merge == "lowest"
473
+ end
474
+
475
+ def mix?
476
+ @merge_type == "mix"
477
+ end
478
+ def override?
479
+ @merge_type == "override"
480
+ end
481
+
482
+ #
483
+ # Making sure @merge and @merge_type are set to
484
+ # appropriate values.
485
+ #
486
+ def ensure_merge_settings
487
+
488
+ @merge_type = "mix" unless override?
489
+ @merge = "first" unless last? or highest? or lowest?
490
+ end
491
+
492
+ def merge (wiTarget, wiSource)
493
+
494
+ return wiSource unless wiTarget
495
+ return wiTarget unless wiSource
496
+
497
+ return wiSource if override?
498
+
499
+ wiSource.attributes.each do | k, v |
500
+
501
+ #puts "merge() '#{k}' => '#{v}'"
502
+
503
+ nk = OpenWFE::fulldup(k)
504
+ nv = OpenWFE::fulldup(v)
505
+
506
+ wiTarget.attributes[nk] = nv
507
+ end
508
+
509
+ return wiTarget
510
+ end
511
+ end
273
512
 
274
- wiSource.attributes.each do | k, v |
275
- wiTarget.attributes[k.dup] = v.dup
276
- end
277
513
  end
278
514
 
279
515
  end
@@ -39,8 +39,9 @@
39
39
  # John Mettraux at openwfe.org
40
40
  #
41
41
 
42
+ require 'openwfe/expressions/condition'
43
+ require 'openwfe/expressions/wtemplate'
42
44
  require 'openwfe/expressions/flowexpression'
43
- require 'openwfe/expressions/fe_condition'
44
45
 
45
46
 
46
47
  #
@@ -69,6 +70,7 @@ module OpenWFE
69
70
  class CursorExpression < WithTemplateExpression
70
71
 
71
72
  attr_accessor \
73
+ :loop_id,
72
74
  :current_child_id,
73
75
  :current_child_fei
74
76
 
@@ -77,6 +79,8 @@ module OpenWFE
77
79
 
78
80
  def apply (workitem)
79
81
 
82
+ @loop_id = 0
83
+
80
84
  @current_child_id = -1
81
85
 
82
86
  clean_children_list()
@@ -119,6 +123,7 @@ module OpenWFE
119
123
  reply_to_parent(workitem)
120
124
  return
121
125
  end
126
+ @loop_id += 1
122
127
  @current_child_id = 0
123
128
  end
124
129
  end
@@ -134,8 +139,8 @@ module OpenWFE
134
139
  #
135
140
  # launch the next child as a template
136
141
 
137
- get_expression_pool\
138
- .launch_template(self, @current_child_fei, workitem, nil)
142
+ get_expression_pool.launch_template(
143
+ self, @loop_id, @current_child_fei, workitem, nil)
139
144
  end
140
145
 
141
146
  #
@@ -39,30 +39,61 @@
39
39
  # John Mettraux at openwfe.org
40
40
  #
41
41
 
42
+ require 'openwfe/expressions/flowexpression'
43
+
44
+
45
+ #
46
+ # do / redo / undo
47
+ #
42
48
 
43
49
  module OpenWFE
44
50
 
45
51
  #
46
- # A simple mixin for adding a stopped instance variable and some
47
- # methods around it to a class (or an object).
52
+ # in preparation.
48
53
  #
49
- module Stoppable
54
+ class DoExpression < FlowExpression
55
+
56
+ attr_accessor \
57
+ :name
50
58
 
51
- def initialize
52
- @stopped = false
59
+ #
60
+ # apply / reply
61
+
62
+ def apply (workitem)
53
63
  end
54
64
 
55
- def do_stop
56
- @stopped = true
65
+ def reply (workitem)
57
66
  end
67
+ end
68
+
69
+ #
70
+ # in preparation.
71
+ #
72
+ class UndoExpression < FlowExpression
73
+
74
+ #
75
+ # apply / reply
58
76
 
59
- def do_restart
60
- @stopped = false
77
+ def apply (workitem)
61
78
  end
62
79
 
63
- def is_stopped?
64
- @stopped
80
+ #def reply (workitem)
81
+ #end
82
+ end
83
+
84
+ #
85
+ # in preparation.
86
+ #
87
+ class RedoExpression < FlowExpression
88
+
89
+ #
90
+ # apply / reply
91
+
92
+ def apply (workitem)
65
93
  end
94
+
95
+ #def reply (workitem)
96
+ #end
66
97
  end
67
98
 
68
99
  end
@@ -64,7 +64,8 @@ module OpenWFE
64
64
  #
65
65
  class IteratorExpression < WithTemplateExpression
66
66
 
67
- attr_accessor :iterator
67
+ attr_accessor \
68
+ :iterator
68
69
 
69
70
  def apply (workitem)
70
71
 
@@ -89,8 +90,8 @@ module OpenWFE
89
90
 
90
91
  store_itself()
91
92
 
92
- get_expression_pool\
93
- .launch_template(self, @children[0], workitem, nil)
93
+ get_expression_pool.launch_template(
94
+ self, 0, @children[0], workitem, nil)
94
95
  end
95
96
  end
96
97
 
@@ -88,11 +88,12 @@ module OpenWFE
88
88
  code = OpenWFE::lookup_vf_attribute(self, workitem, 'code')
89
89
 
90
90
  code = OpenWFE::fetch_text_content(self, workitem, escape) \
91
- if not code
91
+ unless code
92
92
 
93
93
  result = eval(code.to_s)
94
94
 
95
- OpenWFE::set_result(workitem, result) if result != nil
95
+ OpenWFE::set_result(workitem, result) \
96
+ if result != nil # 'false' is a valid result
96
97
 
97
98
  reply_to_parent(workitem)
98
99
  end
@@ -69,6 +69,8 @@ module OpenWFE
69
69
 
70
70
  def apply (workitem)
71
71
 
72
+ remove_timedout_flag(workitem)
73
+
72
74
  @applied_workitem = workitem.dup
73
75
 
74
76
  @participant_name = OpenWFE::lookup_ref(self, workitem)
@@ -122,6 +124,9 @@ module OpenWFE
122
124
  # so that cancel won't unschedule without need
123
125
 
124
126
  cancel()
127
+
128
+ set_timedout_flag(@applied_workitem)
129
+
125
130
  reply_to_parent(@applied_workitem)
126
131
  rescue
127
132
  lerror do
@@ -107,8 +107,15 @@ module OpenWFE
107
107
  #
108
108
  # is it a subprocess ?
109
109
 
110
- template = get_participant_map.lookup_participant(exp_name) \
111
- if (not template) and (not exp_class)
110
+ if (not template) and (not exp_class)
111
+
112
+ template = get_participant_map.lookup_participant(exp_name)
113
+
114
+ unless template
115
+ exp_name = OpenWFE::to_underscore(exp_name)
116
+ template = get_participant_map.lookup_participant(exp_name)
117
+ end
118
+ end
112
119
  #
113
120
  # is it a directly a participant ?
114
121
 
@@ -167,6 +174,7 @@ module OpenWFE
167
174
 
168
175
  get_expression_pool().launch_template(
169
176
  self,
177
+ 0,
170
178
  template,
171
179
  workitem,
172
180
  lookup_attributes(workitem))
@@ -93,7 +93,7 @@ module OpenWFE
93
93
  #puts " ... params are #{params.to_s}"
94
94
 
95
95
  get_expression_pool()\
96
- .launch_template(requester, template_fei, workitem, params)
96
+ .launch_template(requester, 0, template_fei, workitem, params)
97
97
  end
98
98
 
99
99
  #def reply (workitem)
@@ -44,7 +44,7 @@ require 'openwfe/utils'
44
44
  require 'openwfe/util/otime'
45
45
  require 'openwfe/util/scheduler'
46
46
  require 'openwfe/expressions/timeout'
47
- require 'openwfe/expressions/fe_condition'
47
+ require 'openwfe/expressions/condition'
48
48
 
49
49
 
50
50
  #
@@ -108,31 +108,31 @@ module OpenWFE
108
108
  :awakening_time
109
109
 
110
110
  def apply (workitem)
111
- synchronize do
111
+ #synchronize do
112
112
 
113
- sfor = lookup_attribute(:for, workitem)
114
- suntil = lookup_attribute(:until, workitem)
113
+ sfor = lookup_attribute(:for, workitem)
114
+ suntil = lookup_attribute(:until, workitem)
115
115
 
116
- tuntil = nil
116
+ tuntil = nil
117
117
 
118
- if suntil
119
- tuntil = suntil
120
- elsif sfor
121
- tfor = OpenWFE::parse_time_string(sfor)
122
- ldebug { "apply() tfor is '#{tfor}'" }
123
- tuntil = Time.new.to_f + tfor
124
- end
118
+ if suntil
119
+ tuntil = suntil
120
+ elsif sfor
121
+ tfor = OpenWFE::parse_time_string(sfor)
122
+ ldebug { "apply() tfor is '#{tfor}'" }
123
+ tuntil = Time.new.to_f + tfor
124
+ end
125
125
 
126
- if not tuntil
127
- reply_to_parent(workitem)
128
- return
129
- end
126
+ if not tuntil
127
+ reply_to_parent(workitem)
128
+ return
129
+ end
130
130
 
131
- @awakening_time = tuntil
132
- @applied_workitem = workitem.dup
131
+ @awakening_time = tuntil
132
+ @applied_workitem = workitem.dup
133
133
 
134
- reschedule(get_scheduler)
135
- end
134
+ reschedule(get_scheduler)
135
+ #end
136
136
  end
137
137
 
138
138
  #def reply (workitem)
@@ -159,6 +159,8 @@ module OpenWFE
159
159
  #
160
160
  def reschedule (scheduler)
161
161
 
162
+ return unless @awakening_time
163
+
162
164
  ldebug do
163
165
  "[re]schedule() " +
164
166
  "will sleep until '#{@awakening_time}' " +
@@ -197,10 +199,12 @@ module OpenWFE
197
199
  class CronExpression < TimeExpression
198
200
 
199
201
  attr_accessor \
200
- :raw_child, :tab, :name
202
+ :raw_child, :tab, :name, :counter
201
203
 
202
204
  def apply (workitem)
203
205
 
206
+ @counter = 0
207
+
204
208
  if @children.size < 1
205
209
  reply_to_parent(workitem)
206
210
  return
@@ -229,7 +233,7 @@ module OpenWFE
229
233
  # (have to do it after the reschedule, so that the schedule
230
234
  # info is stored within the variable)
231
235
 
232
- set_variable(@name, self)
236
+ set_variable(@name, self) if @name
233
237
 
234
238
  #
235
239
  # resume flow
@@ -260,8 +264,17 @@ module OpenWFE
260
264
  @raw_child.application_context = @application_context
261
265
 
262
266
  begin
267
+
263
268
  get_expression_pool.launch_template(
264
- @fei.wfid, @raw_child, @applied_workitem.dup)
269
+ @fei.wfid, @counter, @raw_child, @applied_workitem.dup)
270
+
271
+ #
272
+ # update count and store self
273
+
274
+ @counter += 1
275
+
276
+ set_variable(@name, self) if @name
277
+
265
278
  rescue
266
279
  lerror do
267
280
  "trigger() cron caught exception\n"+
@@ -277,6 +290,8 @@ module OpenWFE
277
290
  #
278
291
  def reschedule (scheduler)
279
292
 
293
+ #return unless @applied_workitem
294
+
280
295
  @scheduler_job_id = @name.dup
281
296
 
282
297
  @scheduler_job_id = "#{@fei.wfid}__#{@scheduler_job_id}" \
@@ -314,6 +329,8 @@ module OpenWFE
314
329
 
315
330
  def apply (workitem)
316
331
 
332
+ remove_timedout_flag(workitem)
333
+
317
334
  if @children.size < 1
318
335
  reply_to_parent(workitem)
319
336
  return
@@ -366,6 +383,7 @@ module OpenWFE
366
383
  #
367
384
  # do timeout...
368
385
  #
386
+ set_timedout_flag(@applied_workitem)
369
387
  reply_to_parent(@applied_workitem)
370
388
  return
371
389
  end
@@ -377,6 +395,8 @@ module OpenWFE
377
395
 
378
396
  def reschedule (scheduler)
379
397
 
398
+ #return unless @applied_workitem
399
+
380
400
  @scheduler_job_id =
381
401
  scheduler.schedule_in(@frequency, self, nil)
382
402