weel 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|