weel 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/AUTHORS +2 -0
- data/COPYING +504 -0
- data/FEATURES +20 -0
- data/INSTALL +7 -0
- data/README +4 -0
- data/Rakefile +15 -0
- data/example/SimpleHandlerWrapper.rb +68 -0
- data/example/SimpleWorkflow.rb +15 -0
- data/example/runme.rb +12 -0
- data/lib/weel.rb +749 -0
- data/test/ContinueTest.rb +25 -0
- data/test/TestHandlerWrapper.rb +120 -0
- data/test/TestWorkflow.rb +35 -0
- data/test/basic/tc_choose.rb +82 -0
- data/test/basic/tc_codereplace.rb +38 -0
- data/test/basic/tc_data.rb +32 -0
- data/test/basic/tc_endpoint.rb +40 -0
- data/test/basic/tc_handler.rb +19 -0
- data/test/basic/tc_parallel.rb +115 -0
- data/test/basic/tc_search.rb +37 -0
- data/test/basic/tc_state.rb +17 -0
- data/test/basic/tc_wf_control.rb +68 -0
- data/test/complex/tc_generalsynchonizingmerge_loopsearch.rb +113 -0
- data/test/complex/tc_parallel_stop.rb +32 -0
- data/test/wfp_adv_branching/tc_generalizedjoin.rb +11 -0
- data/test/wfp_adv_branching/tc_generalsynchronizingmerge.rb +45 -0
- data/test/wfp_adv_branching/tc_localsynchronizingmerge.rb +37 -0
- data/test/wfp_adv_branching/tc_multichoice_structuredsynchronizingmerge.rb +47 -0
- data/test/wfp_adv_branching/tc_multimerge.rb +11 -0
- data/test/wfp_adv_branching/tc_structured_discriminator.rb +28 -0
- data/test/wfp_adv_branching/tc_structured_partial_join.rb +41 -0
- data/test/wfp_adv_branching/tc_threadmerge.rb +11 -0
- data/test/wfp_adv_branching/tc_threadsplit.rb +11 -0
- data/test/wfp_basic/tc_exclusivechoice_simplemerge.rb +22 -0
- data/test/wfp_basic/tc_parallelsplit_synchronization.rb +26 -0
- data/test/wfp_basic/tc_sequence.rb +16 -0
- data/test/wfp_iteration/tc_structuredloop.rb +65 -0
- data/test/wfp_state_based/tc_deferredchoice.rb +38 -0
- data/test/wfp_state_based/tc_interleavedparallelrouting.rb +30 -0
- data/weel.gemspec +25 -0
- metadata +127 -0
data/FEATURES
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
WEEL Features
|
2
|
+
* Reuses an existing virtual machine for executing control flow
|
3
|
+
* Has a core size of ~ 700 LOC.
|
4
|
+
* Uses ~ 2 MiB of RAM per instance.
|
5
|
+
* Can utilize multiple threads per instance.
|
6
|
+
* Cold starts an instance in less than 2ms.
|
7
|
+
* Provides a DSL for defining processes.
|
8
|
+
* It provides better coverage of workflow patterns compared to Oracle BPELPM,
|
9
|
+
jBOSS jBPM, and Apache Ode.
|
10
|
+
* Supports BPEL and other languages through transformation to a directly
|
11
|
+
executable DSL.
|
12
|
+
* Pluggable event & protocol backend for activities.
|
13
|
+
|
14
|
+
WEEL Applied Benefits
|
15
|
+
* Repair & adpatation - change description, change thread of control.
|
16
|
+
* Stop & restart process instances.
|
17
|
+
* Pause & restart activities (through flexible HandlerWrapper implementation).
|
18
|
+
* Build your own process engines & process languages.
|
19
|
+
* Never worry about the speed of the engine - updates of ruby interpreter
|
20
|
+
(ruby 1.9, ruby 2.0, jruby) ensure speed.
|
data/INSTALL
ADDED
data/README
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rubygems/package_task'
|
3
|
+
require 'rake/testtask'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.test_files = FileList['./test/*/tc_*.rb']
|
8
|
+
t.verbose = false
|
9
|
+
end
|
10
|
+
|
11
|
+
spec = eval(File.read('weel.gemspec'))
|
12
|
+
Gem::PackageTask.new(spec) do |pkg|
|
13
|
+
pkg.need_zip = true
|
14
|
+
pkg.need_tar = true
|
15
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "pp"
|
2
|
+
|
3
|
+
class SimpleHandlerWrapper < WEEL::HandlerWrapperBase
|
4
|
+
def initialize(args,endpoint=nil,position=nil,continue=nil)
|
5
|
+
@__myhandler_stopped = false
|
6
|
+
@__myhandler_position = position
|
7
|
+
@__myhandler_continue = continue
|
8
|
+
@__myhandler_endpoint = endpoint
|
9
|
+
@__myhandler_returnValue = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
# executes a ws-call to the given endpoint with the given parameters. the call
|
13
|
+
# can be executed asynchron, see finished_call & return_value
|
14
|
+
def activity_handle(passthrough, parameters)
|
15
|
+
puts "Handle call:"
|
16
|
+
puts " position=[#{@__myhandler_position}]"
|
17
|
+
puts " passthrough=[#{passthrough}]"
|
18
|
+
puts " endpoint=[#{@__myhandler_endpoint}]"
|
19
|
+
print " parameters="
|
20
|
+
pp parameters
|
21
|
+
@__myhandler_returnValue = 'Handler_Dummy_Result'
|
22
|
+
@__myhandler_continue.continue
|
23
|
+
end
|
24
|
+
|
25
|
+
# returns the result of the last handled call
|
26
|
+
def activity_result_value
|
27
|
+
@__myhandler_returnValue
|
28
|
+
end
|
29
|
+
# Called if the WS-Call should be interrupted. The decision how to deal
|
30
|
+
# with this situation is given to the handler. To provide the possibility
|
31
|
+
# of a continue the Handler will be asked for a passthrough
|
32
|
+
def activity_stop
|
33
|
+
puts "Handler: Recieved stop signal, deciding if stopping\n"
|
34
|
+
@__myhandler_stopped = true
|
35
|
+
end
|
36
|
+
# is called from WEEL after stop_call to ask for a passthrough-value that may give
|
37
|
+
# information about how to continue the call. This passthrough-value is given
|
38
|
+
# to activity_handle if the workflow is configured to do so.
|
39
|
+
def activity_passthrough_value
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
|
43
|
+
# Called if the execution of the actual activity_handle is not necessary anymore
|
44
|
+
# It is definit that the call will not be continued.
|
45
|
+
# At this stage, this is only the case if parallel branches are not needed
|
46
|
+
# anymore to continue the workflow
|
47
|
+
def activity_no_longer_necessary
|
48
|
+
puts "Handler: Recieved no_longer_necessary signal, deciding if stopping\n"
|
49
|
+
@__myhandler_stopped = true
|
50
|
+
end
|
51
|
+
# Is called if a Activity is executed correctly
|
52
|
+
def inform_activity_done
|
53
|
+
puts "Activity #{@__myhandler_position} done\n"
|
54
|
+
end
|
55
|
+
# Is called if a Activity is executed with an error
|
56
|
+
def inform_activity_failed(err)
|
57
|
+
puts "Activity #{@__myhandler_position} failed with error #{err}\n"
|
58
|
+
raise(err)
|
59
|
+
end
|
60
|
+
def inform_syntax_error(err,code)
|
61
|
+
puts "Syntax messed with error #{code}:#{err}\n"
|
62
|
+
raise(err)
|
63
|
+
end
|
64
|
+
def inform_state_change(newstate)
|
65
|
+
puts "State changed to #{newstate}.\n"
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require ::File.dirname(__FILE__) + '/../lib/weel'
|
2
|
+
require ::File.dirname(__FILE__) + '/SimpleHandlerWrapper'
|
3
|
+
|
4
|
+
class SimpleWorkflow < WEEL
|
5
|
+
handlerwrapper SimpleHandlerWrapper
|
6
|
+
|
7
|
+
endpoint :ep1 => "orf.at"
|
8
|
+
data :a => 17
|
9
|
+
|
10
|
+
control flow do
|
11
|
+
activity :a1, :call, :ep1, :a => data.a, :b => 2 do
|
12
|
+
data.a += 3
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/example/runme.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require ::File.dirname(__FILE__) + '/SimpleWorkflow'
|
3
|
+
|
4
|
+
t = SimpleWorkflow.new
|
5
|
+
execution = t.start
|
6
|
+
execution.join()
|
7
|
+
puts "========> Ending-Result:"
|
8
|
+
puts " data:#{t.data.inspect}"
|
9
|
+
puts " status:#{t.status.inspect}"
|
10
|
+
puts " state:#{t.state}"
|
11
|
+
|
12
|
+
|
data/lib/weel.rb
ADDED
@@ -0,0 +1,749 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
# OMG!111! strings have to be emptied
|
4
|
+
class String # {{{
|
5
|
+
def clear
|
6
|
+
self.slice!(0..-1)
|
7
|
+
end
|
8
|
+
end # }}}
|
9
|
+
|
10
|
+
class WEEL
|
11
|
+
def initialize(*args)# {{{
|
12
|
+
@wfsource = nil
|
13
|
+
@dslr = DSLRealization.new
|
14
|
+
@dslr.__weel_handlerwrapper_args = args
|
15
|
+
|
16
|
+
### 1.8
|
17
|
+
initialize_search if methods.include?('initialize_search')
|
18
|
+
initialize_data if methods.include?('initialize_data')
|
19
|
+
initialize_endpoints if methods.include?('initialize_endpoints')
|
20
|
+
initialize_handlerwrapper if methods.include?('initialize_handlerwrapper')
|
21
|
+
initialize_control if methods.include?('initialize_control')
|
22
|
+
### 1.9
|
23
|
+
initialize_search if methods.include?(:initialize_search)
|
24
|
+
initialize_data if methods.include?(:initialize_data)
|
25
|
+
initialize_endpoints if methods.include?(:initialize_endpoints)
|
26
|
+
initialize_handlerwrapper if methods.include?(:initialize_handlerwrapper)
|
27
|
+
initialize_control if methods.include?(:initialize_control)
|
28
|
+
end # }}}
|
29
|
+
|
30
|
+
module Signal # {{{
|
31
|
+
class SkipManipulate < Exception; end
|
32
|
+
class StopSkipManipulate < Exception; end
|
33
|
+
class Stop < Exception; end
|
34
|
+
class Proceed < Exception; end
|
35
|
+
class NoLongerNecessary < Exception; end
|
36
|
+
end # }}}
|
37
|
+
|
38
|
+
class ManipulateRealization # {{{
|
39
|
+
def initialize(data,endpoints,status)
|
40
|
+
@__weel_data = data
|
41
|
+
@__weel_endpoints = endpoints
|
42
|
+
@__weel_status = status
|
43
|
+
@changed_status = status.id
|
44
|
+
@changed_data = []
|
45
|
+
@changed_endpoints = []
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :changed_data, :changed_endpoints
|
49
|
+
|
50
|
+
def changed_status
|
51
|
+
@changed_status != status.id
|
52
|
+
end
|
53
|
+
|
54
|
+
def data
|
55
|
+
ManipulateHash.new(@__weel_data,@changed_data)
|
56
|
+
end
|
57
|
+
def endpoints
|
58
|
+
ManipulateHash.new(@__weel_endpoints,@changed_endpoints)
|
59
|
+
end
|
60
|
+
def status
|
61
|
+
@__weel_status
|
62
|
+
end
|
63
|
+
end # }}}
|
64
|
+
class ManipulateHash # {{{
|
65
|
+
def initialize(values,what)
|
66
|
+
@__weel_values = values
|
67
|
+
@__weel_what = what
|
68
|
+
end
|
69
|
+
|
70
|
+
def delete(value)
|
71
|
+
if @__weel_values.has_key?(value)
|
72
|
+
@__weel_what << value
|
73
|
+
@__weel_values.delete(value)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def clear
|
78
|
+
@__weel_what += @__weel_values.keys
|
79
|
+
@__weel_values.clear
|
80
|
+
end
|
81
|
+
|
82
|
+
def method_missing(name,*args)
|
83
|
+
|
84
|
+
if args.empty? && @__weel_values.has_key?(name)
|
85
|
+
@__weel_values[name]
|
86
|
+
elsif name.to_s[-1..-1] == "=" && args.length == 1
|
87
|
+
temp = name.to_s[0..-2]
|
88
|
+
@__weel_what << temp.to_sym
|
89
|
+
@__weel_values[temp.to_sym] = args[0]
|
90
|
+
elsif name.to_s == "[]=" && args.length == 2
|
91
|
+
@__weel_values[args[0]] = args[1]
|
92
|
+
elsif name.to_s == "[]" && args.length == 1
|
93
|
+
@__weel_values[args[0]]
|
94
|
+
else
|
95
|
+
nil
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end # }}}
|
99
|
+
|
100
|
+
class Status #{{{
|
101
|
+
def initialize(id,message)
|
102
|
+
@id = id
|
103
|
+
@message = message
|
104
|
+
end
|
105
|
+
def update(id,message)
|
106
|
+
@id = id
|
107
|
+
@message = message
|
108
|
+
end
|
109
|
+
attr_reader :id, :message
|
110
|
+
end #}}}
|
111
|
+
|
112
|
+
class ReadHash # {{{
|
113
|
+
def initialize(values)
|
114
|
+
@__weel_values = values
|
115
|
+
end
|
116
|
+
|
117
|
+
def method_missing(name,*args)
|
118
|
+
temp = nil
|
119
|
+
if args.empty? && @__weel_values.has_key?(name)
|
120
|
+
@__weel_values[name]
|
121
|
+
#TODO dont let user change stuff
|
122
|
+
else
|
123
|
+
nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end # }}}
|
127
|
+
|
128
|
+
class HandlerWrapperBase # {{{
|
129
|
+
def initialize(arguments,endpoint=nil,position=nil,continue=nil); end
|
130
|
+
|
131
|
+
def activity_handle(passthrough, endpoint, parameters); end
|
132
|
+
|
133
|
+
def activity_result_value; end
|
134
|
+
def activity_result_status; end
|
135
|
+
|
136
|
+
def activity_stop; end
|
137
|
+
def activity_passthrough_value; end
|
138
|
+
|
139
|
+
def activity_no_longer_necessary; end
|
140
|
+
|
141
|
+
def inform_activity_done; end
|
142
|
+
def inform_activity_manipulate; end
|
143
|
+
def inform_activity_failed(err); end
|
144
|
+
|
145
|
+
def inform_syntax_error(err,code); end
|
146
|
+
def inform_manipulate_change(status,data,endpoints); end
|
147
|
+
def inform_position_change(ipc); end
|
148
|
+
def inform_state_change(newstate); end
|
149
|
+
|
150
|
+
def vote_sync_before; true; end
|
151
|
+
def vote_sync_after; true; end
|
152
|
+
|
153
|
+
def callback(result); end
|
154
|
+
end # }}}
|
155
|
+
|
156
|
+
class Position # {{{
|
157
|
+
attr_reader :position
|
158
|
+
attr_accessor :detail, :passthrough
|
159
|
+
def initialize(position, detail=:at, passthrough=nil) # :at or :after or :unmark
|
160
|
+
@position = position
|
161
|
+
@detail = detail
|
162
|
+
@passthrough = passthrough
|
163
|
+
end
|
164
|
+
end # }}}
|
165
|
+
|
166
|
+
class Continue #{{{
|
167
|
+
def initialize
|
168
|
+
@thread = Thread.new{Thread.stop}
|
169
|
+
end
|
170
|
+
def waiting?
|
171
|
+
@thread.alive?
|
172
|
+
end
|
173
|
+
def continue
|
174
|
+
while @thread.status != 'sleep' && @thread.alive?
|
175
|
+
Thread.pass
|
176
|
+
end
|
177
|
+
@thread.wakeup if @thread.alive?
|
178
|
+
end
|
179
|
+
def wait
|
180
|
+
@thread.join
|
181
|
+
end
|
182
|
+
end #}}}
|
183
|
+
|
184
|
+
def self::search(weel_search)# {{{
|
185
|
+
define_method :initialize_search do
|
186
|
+
self.search weel_search
|
187
|
+
end
|
188
|
+
end # }}}
|
189
|
+
def self::endpoint(new_endpoints)# {{{
|
190
|
+
@@__weel_new_endpoints ||= {}
|
191
|
+
@@__weel_new_endpoints.merge! new_endpoints
|
192
|
+
define_method :initialize_endpoints do
|
193
|
+
@@__weel_new_endpoints.each do |name,value|
|
194
|
+
@dslr.__weel_endpoints[name.to_s.to_sym] = value
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end # }}}
|
198
|
+
def self::data(data_elements)# {{{
|
199
|
+
@@__weel_new_data_elements ||= {}
|
200
|
+
@@__weel_new_data_elements.merge! data_elements
|
201
|
+
define_method :initialize_data do
|
202
|
+
@@__weel_new_data_elements.each do |name,value|
|
203
|
+
@dslr.__weel_data[name.to_s.to_sym] = value
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end # }}}
|
207
|
+
def self::handlerwrapper(aClassname, *args)# {{{
|
208
|
+
define_method :initialize_handlerwrapper do
|
209
|
+
self.handlerwrapper = aClassname
|
210
|
+
self.handlerwrapper_args = args unless args.empty?
|
211
|
+
end
|
212
|
+
end # }}}
|
213
|
+
def self::control(flow, &block)# {{{
|
214
|
+
@@__weel_control_block = block
|
215
|
+
define_method :initialize_control do
|
216
|
+
self.description(&(@@__weel_control_block))
|
217
|
+
end
|
218
|
+
end # }}}
|
219
|
+
def self::flow #{{{
|
220
|
+
end #}}}
|
221
|
+
|
222
|
+
class DSLRealization # {{{
|
223
|
+
def initialize
|
224
|
+
@__weel_search_positions = {}
|
225
|
+
@__weel_positions = Array.new
|
226
|
+
@__weel_main = nil
|
227
|
+
@__weel_data ||= Hash.new
|
228
|
+
@__weel_endpoints ||= Hash.new
|
229
|
+
@__weel_handlerwrapper = HandlerWrapperBase
|
230
|
+
@__weel_handlerwrapper_args = []
|
231
|
+
@__weel_state = :ready
|
232
|
+
@__weel_status = Status.new(0,"undefined")
|
233
|
+
end
|
234
|
+
attr_accessor :__weel_search_positions, :__weel_positions, :__weel_main, :__weel_data, :__weel_endpoints, :__weel_handlerwrapper, :__weel_handlerwrapper_args
|
235
|
+
attr_reader :__weel_state, :__weel_status
|
236
|
+
|
237
|
+
# DSL-Construct for an atomic activity
|
238
|
+
# position: a unique identifier within the wf-description (may be used by the search to identify a starting point
|
239
|
+
# type:
|
240
|
+
# - :manipulate - just yield a given block
|
241
|
+
# - :call - order the handlerwrapper to perform a service call
|
242
|
+
# endpoint: (only with :call) ep of the service
|
243
|
+
# parameters: (only with :call) service parameters
|
244
|
+
def activity(position, type, endpoint=nil, *parameters, &blk)# {{{
|
245
|
+
position = __weel_position_test position
|
246
|
+
begin
|
247
|
+
searchmode = __weel_is_in_search_mode(position)
|
248
|
+
return if searchmode == true
|
249
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
250
|
+
|
251
|
+
Thread.current[:continue] = Continue.new
|
252
|
+
handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args, @__weel_endpoints[endpoint], position, Thread.current[:continue]
|
253
|
+
|
254
|
+
ipc = {}
|
255
|
+
if searchmode == :after
|
256
|
+
wp = WEEL::Position.new(position, :after, nil)
|
257
|
+
ipc[:after] = [wp.position]
|
258
|
+
else
|
259
|
+
if Thread.current[:branch_parent] && Thread.current[:branch_parent][:branch_position]
|
260
|
+
@__weel_positions.delete Thread.current[:branch_parent][:branch_position]
|
261
|
+
ipc[:unmark] ||= []
|
262
|
+
ipc[:unmark] << Thread.current[:branch_parent][:branch_position].position rescue nil
|
263
|
+
Thread.current[:branch_parent][:branch_position] = nil
|
264
|
+
end
|
265
|
+
if Thread.current[:branch_position]
|
266
|
+
@__weel_positions.delete Thread.current[:branch_position]
|
267
|
+
ipc[:unmark] ||= []
|
268
|
+
ipc[:unmark] << Thread.current[:branch_position].position rescue nil
|
269
|
+
end
|
270
|
+
wp = WEEL::Position.new(position, :at, nil)
|
271
|
+
ipc[:at] = [wp.position]
|
272
|
+
end
|
273
|
+
@__weel_positions << wp
|
274
|
+
Thread.current[:branch_position] = wp
|
275
|
+
|
276
|
+
handlerwrapper.inform_position_change(ipc)
|
277
|
+
|
278
|
+
# searchmode position is after, jump directly to vote_sync_after
|
279
|
+
raise Signal::Proceed if searchmode == :after
|
280
|
+
|
281
|
+
raise Signal::Stop unless handlerwrapper.vote_sync_before
|
282
|
+
|
283
|
+
case type
|
284
|
+
when :manipulate
|
285
|
+
if block_given?
|
286
|
+
handlerwrapper.inform_activity_manipulate
|
287
|
+
mr = ManipulateRealization.new(@__weel_data,@__weel_endpoints,@__weel_status)
|
288
|
+
status = nil
|
289
|
+
parameters.delete_if do |para|
|
290
|
+
status = para if para.is_a?(Status)
|
291
|
+
para.is_a?(Status)
|
292
|
+
end
|
293
|
+
case blk.arity
|
294
|
+
when 1; mr.instance_exec(parameters,&blk)
|
295
|
+
when 2; mr.instance_exec(parameters,status,&blk)
|
296
|
+
else
|
297
|
+
mr.instance_eval(&blk)
|
298
|
+
end
|
299
|
+
handlerwrapper.inform_manipulate_change(
|
300
|
+
(mr.changed_status ? @__weel_status : nil),
|
301
|
+
(mr.changed_data.any? ? mr.changed_data.uniq : nil),
|
302
|
+
(mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil)
|
303
|
+
)
|
304
|
+
handlerwrapper.inform_activity_done
|
305
|
+
wp.detail = :after
|
306
|
+
handlerwrapper.inform_position_change :after => [wp.position]
|
307
|
+
end
|
308
|
+
when :call
|
309
|
+
params = { }
|
310
|
+
passthrough = @__weel_search_positions[position] ? @__weel_search_positions[position].passthrough : nil
|
311
|
+
parameters.each do |p|
|
312
|
+
if p.class == Hash && parameters.length == 1
|
313
|
+
params = p
|
314
|
+
else
|
315
|
+
if !p.is_a?(Symbol) || !@__weel_data.include?(p)
|
316
|
+
raise("not all passed parameters are data elements")
|
317
|
+
end
|
318
|
+
params[p] = @__weel_data[p]
|
319
|
+
end
|
320
|
+
end
|
321
|
+
# handshake call and wait until it finished
|
322
|
+
handlerwrapper.activity_handle passthrough, params
|
323
|
+
Thread.current[:continue].wait unless Thread.current[:nolongernecessary] || self.__weel_state == :stopping || self.__weel_state == :stopped
|
324
|
+
|
325
|
+
if Thread.current[:nolongernecessary]
|
326
|
+
handlerwrapper.activity_no_longer_necessary
|
327
|
+
raise Signal::NoLongerNecessary
|
328
|
+
end
|
329
|
+
if self.__weel_state == :stopping
|
330
|
+
handlerwrapper.activity_stop
|
331
|
+
wp.passthrough = handlerwrapper.activity_passthrough_value
|
332
|
+
end
|
333
|
+
|
334
|
+
if wp.passthrough.nil? && block_given?
|
335
|
+
handlerwrapper.inform_activity_manipulate
|
336
|
+
mr = ManipulateRealization.new(@__weel_data,@__weel_endpoints,@__weel_status)
|
337
|
+
status = handlerwrapper.activity_result_status
|
338
|
+
case blk.arity
|
339
|
+
when 1; mr.instance_exec(handlerwrapper.activity_result_value,&blk)
|
340
|
+
when 2; mr.instance_exec(handlerwrapper.activity_result_value,(status.is_a?(Status)?status:nil),&blk)
|
341
|
+
else
|
342
|
+
mr.instance_eval(&blk)
|
343
|
+
end
|
344
|
+
handlerwrapper.inform_manipulate_change(
|
345
|
+
(mr.changed_status ? @__weel_status : nil),
|
346
|
+
(mr.changed_data.any? ? mr.changed_data.uniq : nil),
|
347
|
+
(mr.changed_endpoints.any? ? mr.changed_endpoints.uniq : nil)
|
348
|
+
)
|
349
|
+
end
|
350
|
+
if wp.passthrough.nil?
|
351
|
+
handlerwrapper.inform_activity_done
|
352
|
+
wp.detail = :after
|
353
|
+
handlerwrapper.inform_position_change :after => [wp.position]
|
354
|
+
end
|
355
|
+
end
|
356
|
+
raise Signal::Proceed
|
357
|
+
rescue Signal::SkipManipulate, Signal::Proceed
|
358
|
+
if self.__weel_state != :stopping && !handlerwrapper.vote_sync_after
|
359
|
+
self.__weel_state = :stopping
|
360
|
+
wp.detail = :unmark
|
361
|
+
end
|
362
|
+
rescue Signal::NoLongerNecessary
|
363
|
+
@__weel_positions.delete wp
|
364
|
+
Thread.current[:branch_position] = nil
|
365
|
+
wp.detail = :unmark
|
366
|
+
handlerwrapper.inform_position_change :unmark => [wp.position]
|
367
|
+
rescue Signal::StopSkipManipulate, Signal::Stop
|
368
|
+
self.__weel_state = :stopping
|
369
|
+
rescue => err
|
370
|
+
handlerwrapper.inform_activity_failed err
|
371
|
+
self.__weel_state = :stopping
|
372
|
+
end
|
373
|
+
end # }}}
|
374
|
+
|
375
|
+
# Parallel DSL-Construct
|
376
|
+
# Defines Workflow paths that can be executed parallel.
|
377
|
+
# May contain multiple branches (parallel_branch)
|
378
|
+
def parallel(type=nil)# {{{
|
379
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
380
|
+
|
381
|
+
Thread.current[:branches] = []
|
382
|
+
Thread.current[:branch_finished_count] = 0
|
383
|
+
Thread.current[:branch_event] = Continue.new
|
384
|
+
Thread.current[:mutex] = Mutex.new
|
385
|
+
yield
|
386
|
+
|
387
|
+
Thread.current[:branch_wait_count] = (type.is_a?(Hash) && type.size == 1 && type[:wait] != nil && (type[:wait].is_a?(Integer)) ? type[:wait] : Thread.current[:branches].size)
|
388
|
+
Thread.current[:branches].each do |thread|
|
389
|
+
while thread.status != 'sleep' && thread.alive?
|
390
|
+
Thread.pass
|
391
|
+
end
|
392
|
+
# decide after executing block in parallel cause for coopis
|
393
|
+
# it goes out of search mode while dynamically counting branches
|
394
|
+
if Thread.current[:branch_search] == false
|
395
|
+
thread[:branch_search] = false
|
396
|
+
end
|
397
|
+
thread.wakeup if thread.alive?
|
398
|
+
end
|
399
|
+
|
400
|
+
Thread.current[:branch_event].wait
|
401
|
+
#Thread.current[:branch_event] = nil
|
402
|
+
|
403
|
+
unless self.__weel_state == :stopping || self.__weel_state == :stopped
|
404
|
+
# first set all to no_longer_neccessary
|
405
|
+
Thread.current[:branches].each do |thread|
|
406
|
+
if thread.alive?
|
407
|
+
thread[:nolongernecessary] = true
|
408
|
+
__weel_recursive_continue(thread)
|
409
|
+
end
|
410
|
+
end
|
411
|
+
# wait for all
|
412
|
+
Thread.current[:branches].each do |thread|
|
413
|
+
__weel_recursive_join(thread)
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end # }}}
|
417
|
+
|
418
|
+
# Defines a branch of a parallel-Construct
|
419
|
+
def parallel_branch(*vars)# {{{
|
420
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
421
|
+
branch_parent = Thread.current
|
422
|
+
Thread.current[:branches] << Thread.new(*vars) do |*local|
|
423
|
+
branch_parent[:mutex].synchronize do
|
424
|
+
Thread.current.abort_on_exception = true
|
425
|
+
Thread.current[:branch_status] = false
|
426
|
+
Thread.current[:branch_parent] = branch_parent
|
427
|
+
if branch_parent[:alternative_executed] && branch_parent[:alternative_executed].length > 0
|
428
|
+
Thread.current[:alternative_executed] = [branch_parent[:alternative_executed].last]
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
432
|
+
Thread.stop
|
433
|
+
yield(*local)
|
434
|
+
|
435
|
+
branch_parent[:mutex].synchronize do
|
436
|
+
Thread.current[:branch_status] = true
|
437
|
+
branch_parent[:branch_finished_count] += 1
|
438
|
+
if branch_parent[:branch_finished_count] == branch_parent[:branch_wait_count] && self.__weel_state != :stopping
|
439
|
+
branch_parent[:branch_event].continue
|
440
|
+
end
|
441
|
+
end
|
442
|
+
if self.__weel_state != :stopping && self.__weel_state != :stopped
|
443
|
+
if Thread.current[:branch_position]
|
444
|
+
@__weel_positions.delete Thread.current[:branch_position]
|
445
|
+
begin
|
446
|
+
ipc = {}
|
447
|
+
ipc[:unmark] = [Thread.current[:branch_position].position]
|
448
|
+
handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
|
449
|
+
handlerwrapper.inform_position_change(ipc)
|
450
|
+
end rescue nil
|
451
|
+
Thread.current[:branch_position] = nil
|
452
|
+
end
|
453
|
+
end
|
454
|
+
end
|
455
|
+
Thread.pass
|
456
|
+
end # }}}
|
457
|
+
|
458
|
+
# Choose DSL-Construct
|
459
|
+
# Defines a choice in the Workflow path.
|
460
|
+
# May contain multiple execution alternatives
|
461
|
+
def choose # {{{
|
462
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
463
|
+
Thread.current[:alternative_executed] ||= []
|
464
|
+
Thread.current[:alternative_executed] << false
|
465
|
+
yield
|
466
|
+
Thread.current[:alternative_executed].pop
|
467
|
+
nil
|
468
|
+
end # }}}
|
469
|
+
|
470
|
+
# Defines a possible choice of a choose-Construct
|
471
|
+
# Block is executed if condition == true or
|
472
|
+
# searchmode is active (to find the starting position)
|
473
|
+
def alternative(condition)# {{{
|
474
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
475
|
+
yield if __weel_is_in_search_mode || condition
|
476
|
+
Thread.current[:alternative_executed][-1] = true if condition
|
477
|
+
end # }}}
|
478
|
+
def otherwise # {{{
|
479
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
480
|
+
yield if __weel_is_in_search_mode || !Thread.current[:alternative_executed].last
|
481
|
+
end # }}}
|
482
|
+
|
483
|
+
# Defines a critical block (=Mutex)
|
484
|
+
def critical(id)# {{{
|
485
|
+
@__weel_critical ||= Mutex.new
|
486
|
+
semaphore = nil
|
487
|
+
@__weel_critical.synchronize do
|
488
|
+
@__weel_critical_sections ||= {}
|
489
|
+
semaphore = @__weel_critical_sections[id] ? @__weel_critical_sections[id] : Mutex.new
|
490
|
+
@__weel_critical_sections[id] = semaphore if id
|
491
|
+
end
|
492
|
+
semaphore.synchronize do
|
493
|
+
yield
|
494
|
+
end
|
495
|
+
end # }}}
|
496
|
+
|
497
|
+
# Defines a Cycle (loop/iteration)
|
498
|
+
def loop(condition)# {{{
|
499
|
+
unless condition.is_a?(Array) && condition[0].is_a?(Proc) && [:pre_test,:post_test].include?(condition[1])
|
500
|
+
raise "condition must be called pre_test{} or post_test{}"
|
501
|
+
end
|
502
|
+
return if self.__weel_state == :stopping || self.__weel_state == :stopped || Thread.current[:nolongernecessary]
|
503
|
+
if __weel_is_in_search_mode
|
504
|
+
yield
|
505
|
+
return if __weel_is_in_search_mode
|
506
|
+
end
|
507
|
+
case condition[1]
|
508
|
+
when :pre_test
|
509
|
+
yield while condition[0].call && self.__weel_state != :stopping && self.__weel_state != :stopped
|
510
|
+
when :post_test
|
511
|
+
begin; yield; end while condition[0].call && self.__weel_state != :stopping && self.__weel_state != :stopped
|
512
|
+
end
|
513
|
+
end # }}}
|
514
|
+
|
515
|
+
def pre_test(&blk)# {{{
|
516
|
+
[blk, :pre_test]
|
517
|
+
end # }}}
|
518
|
+
def post_test(&blk)# {{{
|
519
|
+
[blk, :post_test]
|
520
|
+
end # }}}
|
521
|
+
|
522
|
+
def status # {{{
|
523
|
+
@__weel_status
|
524
|
+
end # }}}
|
525
|
+
def data # {{{
|
526
|
+
ReadHash.new(@__weel_data)
|
527
|
+
end # }}}
|
528
|
+
def endpoints # {{{
|
529
|
+
ReadHash.new(@__weel_endpoints)
|
530
|
+
end # }}}
|
531
|
+
|
532
|
+
private
|
533
|
+
def __weel_recursive_print(thread,indent='')# {{{
|
534
|
+
p "#{indent}#{thread}"
|
535
|
+
if thread[:branches]
|
536
|
+
thread[:branches].each do |b|
|
537
|
+
__weel_recursive_print(b,indent+' ')
|
538
|
+
end
|
539
|
+
end
|
540
|
+
end # }}}
|
541
|
+
def __weel_recursive_continue(thread)# {{{
|
542
|
+
return unless thread
|
543
|
+
if thread.alive? && thread[:continue] && thread[:continue].waiting?
|
544
|
+
thread[:continue].continue
|
545
|
+
end
|
546
|
+
if thread.alive? && thread[:branch_event] && thread[:branch_event].waiting?
|
547
|
+
thread[:mutex].synchronize do
|
548
|
+
unless thread[:branch_event].nil?
|
549
|
+
thread[:branch_event].continue
|
550
|
+
# thread[:branch_event] = nil
|
551
|
+
end
|
552
|
+
end
|
553
|
+
end
|
554
|
+
if thread[:branches]
|
555
|
+
thread[:branches].each do |b|
|
556
|
+
__weel_recursive_continue(b)
|
557
|
+
end
|
558
|
+
end
|
559
|
+
end # }}}
|
560
|
+
def __weel_recursive_join(thread)# {{{
|
561
|
+
return unless thread
|
562
|
+
if thread.alive? && thread != Thread.current
|
563
|
+
thread.join
|
564
|
+
end
|
565
|
+
if thread[:branches]
|
566
|
+
thread[:branches].each do |b|
|
567
|
+
__weel_recursive_join(b)
|
568
|
+
end
|
569
|
+
end
|
570
|
+
end # }}}
|
571
|
+
|
572
|
+
def __weel_position_test(position)# {{{
|
573
|
+
if position.is_a?(Symbol) && position.to_s =~ /[a-zA-Z][a-zA-Z0-9_]*/
|
574
|
+
position
|
575
|
+
else
|
576
|
+
self.__weel_state = :stopping
|
577
|
+
handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
|
578
|
+
handlerwrapper.inform_syntax_error(Exception.new("position (#{position}) not valid"),nil)
|
579
|
+
end
|
580
|
+
end # }}}
|
581
|
+
|
582
|
+
def __weel_is_in_search_mode(position = nil)# {{{
|
583
|
+
branch = Thread.current
|
584
|
+
return false if @__weel_search_positions.empty? || branch[:branch_search] == false
|
585
|
+
|
586
|
+
if position && @__weel_search_positions.include?(position) # matching searchpos => start execution from here
|
587
|
+
branch[:branch_search] = false # execute all activities in THIS branch (thread) after this point
|
588
|
+
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
|
589
|
+
branch = branch[:branch_parent]
|
590
|
+
branch[:branch_search] = false
|
591
|
+
end
|
592
|
+
@__weel_search_positions[position].detail == :after ? :after : false
|
593
|
+
else
|
594
|
+
branch[:branch_search] = true
|
595
|
+
end
|
596
|
+
end # }}}
|
597
|
+
|
598
|
+
public
|
599
|
+
def __weel_finalize
|
600
|
+
__weel_recursive_join(@__weel_main)
|
601
|
+
@__weel_state = :stopped
|
602
|
+
handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
|
603
|
+
handlerwrapper.inform_state_change @__weel_state
|
604
|
+
end
|
605
|
+
|
606
|
+
def __weel_state=(newState)# {{{
|
607
|
+
return @__weel_state if newState == @__weel_state
|
608
|
+
@__weel_positions = Array.new if @__weel_state != newState && newState == :running
|
609
|
+
handlerwrapper = @__weel_handlerwrapper.new @__weel_handlerwrapper_args
|
610
|
+
@__weel_state = newState
|
611
|
+
|
612
|
+
if newState == :stopping
|
613
|
+
__weel_recursive_continue(@__weel_main)
|
614
|
+
end
|
615
|
+
|
616
|
+
handlerwrapper.inform_state_change @__weel_state
|
617
|
+
end # }}}
|
618
|
+
|
619
|
+
end # }}}
|
620
|
+
|
621
|
+
public
|
622
|
+
def positions # {{{
|
623
|
+
@dslr.__weel_positions
|
624
|
+
end # }}}
|
625
|
+
|
626
|
+
# set the handlerwrapper
|
627
|
+
def handlerwrapper # {{{
|
628
|
+
@dslr.__weel_handlerwrapper
|
629
|
+
end # }}}
|
630
|
+
def handlerwrapper=(new_weel_handlerwrapper) # {{{
|
631
|
+
superclass = new_weel_handlerwrapper
|
632
|
+
while superclass
|
633
|
+
check_ok = true if superclass == WEEL::HandlerWrapperBase
|
634
|
+
superclass = superclass.superclass
|
635
|
+
end
|
636
|
+
raise "Handlerwrapper is not inherited from HandlerWrapperBase" unless check_ok
|
637
|
+
@dslr.__weel_handlerwrapper = new_weel_handlerwrapper
|
638
|
+
end # }}}
|
639
|
+
|
640
|
+
# Get/Set the handlerwrapper arguments
|
641
|
+
def handlerwrapper_args # {{{
|
642
|
+
@dslr.__weel_handlerwrapper_args
|
643
|
+
end # }}}
|
644
|
+
def handlerwrapper_args=(args) # {{{
|
645
|
+
if args.class == Array
|
646
|
+
@dslr.__weel_handlerwrapper_args = args
|
647
|
+
end
|
648
|
+
nil
|
649
|
+
end # }}}
|
650
|
+
|
651
|
+
# Get the state of execution (ready|running|stopping|stopped|finished)
|
652
|
+
def state # {{{
|
653
|
+
@dslr.__weel_state
|
654
|
+
end # }}}
|
655
|
+
|
656
|
+
# Set search positions
|
657
|
+
# set new_weel_search to a boolean (or anything else) to start the process from beginning (reset serach positions)
|
658
|
+
def search(new_weel_search=false) # {{{
|
659
|
+
@dslr.__weel_search_positions.clear
|
660
|
+
|
661
|
+
new_weel_search = [new_weel_search] if new_weel_search.is_a?(Position)
|
662
|
+
|
663
|
+
if !new_weel_search.is_a?(Array) || new_weel_search.empty?
|
664
|
+
false
|
665
|
+
else
|
666
|
+
new_weel_search.each do |search_position|
|
667
|
+
@dslr.__weel_search_positions[search_position.position] = search_position
|
668
|
+
end
|
669
|
+
true
|
670
|
+
end
|
671
|
+
end # }}}
|
672
|
+
|
673
|
+
def data(new_data=nil) # {{{
|
674
|
+
unless new_data.nil? || !new_data.is_a?(Hash)
|
675
|
+
new_data.each{|k,v|@dslr.__weel_data[k] = v}
|
676
|
+
end
|
677
|
+
@dslr.__weel_data
|
678
|
+
end # }}}
|
679
|
+
def endpoints(new_endpoints=nil) # {{{
|
680
|
+
unless new_endpoints.nil? || !new_endpoints.is_a?(Hash)
|
681
|
+
new_endpoints.each{|k,v|@dslr.__weel_endpoints[k] = v}
|
682
|
+
end
|
683
|
+
@dslr.__weel_endpoints
|
684
|
+
end # }}}
|
685
|
+
def endpoint(new_endpoints) # {{{
|
686
|
+
unless new_endpoints.nil? || !new_endpoints.is_a?(Hash) || !new_endpoints.length == 1
|
687
|
+
new_endpoints.each{|k,v|@dslr.__weel_endpoints[k] = v}
|
688
|
+
end
|
689
|
+
nil
|
690
|
+
end # }}}
|
691
|
+
def status # {{{
|
692
|
+
@dslr.__weel_status
|
693
|
+
end # }}}
|
694
|
+
|
695
|
+
# get/set workflow description
|
696
|
+
def description(code = nil,&blk) # {{{
|
697
|
+
bgiven = block_given?
|
698
|
+
if code.nil? && !bgiven
|
699
|
+
@wfsource
|
700
|
+
else
|
701
|
+
@wfsource = code unless bgiven
|
702
|
+
(class << self; self; end).class_eval do
|
703
|
+
define_method :__weel_control_flow do
|
704
|
+
@dslr.__weel_positions.clear
|
705
|
+
@dslr.__weel_state = :running
|
706
|
+
begin
|
707
|
+
if bgiven
|
708
|
+
@dslr.instance_eval(&blk)
|
709
|
+
else
|
710
|
+
@dslr.instance_eval(code)
|
711
|
+
end
|
712
|
+
rescue Exception => err
|
713
|
+
@dslr.__weel_state = :stopping
|
714
|
+
handlerwrapper = @dslr.__weel_handlerwrapper.new @dslr.__weel_handlerwrapper_args
|
715
|
+
handlerwrapper.inform_syntax_error(err,code)
|
716
|
+
end
|
717
|
+
if @dslr.__weel_state == :running
|
718
|
+
@dslr.__weel_state = :finished
|
719
|
+
ipc = { :unmark => [] }
|
720
|
+
@dslr.__weel_positions.each{|wp| ipc[:unmark] << wp.position}
|
721
|
+
@dslr.__weel_positions.clear
|
722
|
+
handlerwrapper = @dslr.__weel_handlerwrapper.new @dslr.__weel_handlerwrapper_args
|
723
|
+
handlerwrapper.inform_position_change(ipc)
|
724
|
+
end
|
725
|
+
if @dslr.__weel_state == :stopping
|
726
|
+
@dslr.__weel_finalize
|
727
|
+
end
|
728
|
+
end
|
729
|
+
end
|
730
|
+
bgiven ? blk : code
|
731
|
+
end
|
732
|
+
end # }}}
|
733
|
+
|
734
|
+
# Stop the workflow execution
|
735
|
+
def stop # {{{
|
736
|
+
Thread.new do
|
737
|
+
@dslr.__weel_state = :stopping
|
738
|
+
@dslr.__weel_main.join if @dslr.__weel_main
|
739
|
+
end
|
740
|
+
end # }}}
|
741
|
+
# Start the workflow execution
|
742
|
+
def start # {{{
|
743
|
+
return nil if @dslr.__weel_state != :ready && @dslr.__weel_state != :stopped
|
744
|
+
@dslr.__weel_main = Thread.new do
|
745
|
+
__weel_control_flow
|
746
|
+
end
|
747
|
+
end # }}}
|
748
|
+
|
749
|
+
end
|