cpee 2.1.61 → 2.1.62
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.
- checksums.yaml +4 -4
- data/cockpit/js/edit.js +2 -0
- data/cockpit/js/instance.js +5 -1
- data/cpee.gemspec +4 -2
- data/lib/cpee/implementation_properties.rb +1 -0
- data/server/executionhandlers/ruby/connection.rb +22 -102
- data/server/executionhandlers/ruby/controller.rb +0 -1
- data/server/executionhandlers/ruby/dsl_to_dslx.xsl +5 -3
- data/server/executionhandlers/rubyext/backend/README.md +17 -0
- data/server/executionhandlers/rubyext/backend/instance.template +18 -0
- data/server/executionhandlers/rubyext/backend/opts.yaml +8 -0
- data/server/executionhandlers/rubyext/backend/run +38 -0
- data/server/executionhandlers/rubyext/connection.rb +431 -0
- data/server/executionhandlers/rubyext/controller.rb +210 -0
- data/server/executionhandlers/rubyext/dsl_to_dslx.xsl +914 -0
- data/server/executionhandlers/rubyext/execution.rb +84 -0
- data/server/routing/end.pid +1 -1
- data/server/routing/forward-events-00.pid +1 -1
- data/server/routing/forward-votes.pid +1 -1
- data/server/routing/persist.pid +1 -1
- metadata +41 -5
@@ -0,0 +1,431 @@
|
|
1
|
+
# This file is part of CPEE.
|
2
|
+
#
|
3
|
+
# CPEE is free software: you can redistribute it and/or modify it under the terms
|
4
|
+
# of the GNU General Public License as published by the Free Software Foundation,
|
5
|
+
# either version 3 of the License, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# CPEE is distributed in the hope that it will be useful, but WITHOUT ANY
|
8
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
9
|
+
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
10
|
+
#
|
11
|
+
# You should have received a copy of the GNU General Public License along with
|
12
|
+
# CPEE (file COPYING in the main directory). If not, see
|
13
|
+
# <http://www.gnu.org/licenses/>.
|
14
|
+
|
15
|
+
require 'charlock_holmes'
|
16
|
+
require 'mimemagic'
|
17
|
+
require 'base64'
|
18
|
+
require 'get_process_mem'
|
19
|
+
require 'cpee-eval-ruby/translation'
|
20
|
+
|
21
|
+
class ConnectionWrapper < WEEL::ConnectionWrapperBase
|
22
|
+
def self::loop_guard(arguments,id,count) # {{{
|
23
|
+
controller = arguments[0]
|
24
|
+
return false if controller.attributes['nednoamol']
|
25
|
+
tsn = Time.now
|
26
|
+
tso = controller.loop_guard[id][:timestamp] rescue Time.now
|
27
|
+
controller.loop_guard[id] = { :count => count, :timestamp => tsn }
|
28
|
+
# if we have more than 100 loop iterations and the last one took less than 2 seconds, we slow the hell down
|
29
|
+
tso + 2 > tsn && count > 100
|
30
|
+
end # }}}
|
31
|
+
|
32
|
+
def self::inform_state_change(arguments,newstate) # {{{
|
33
|
+
controller = arguments[0]
|
34
|
+
controller.notify("state/change", :state => newstate)
|
35
|
+
end # }}}
|
36
|
+
def self::inform_syntax_error(arguments,err,code)# {{{
|
37
|
+
# TODO extract spot (code) where error happened for better error handling (ruby 3.1 only)
|
38
|
+
# https://github.com/rails/rails/pull/45818/commits/3beb2aff3be712e44c34a588fbf35b79c0246ca5
|
39
|
+
puts err.message
|
40
|
+
puts err.backtrace
|
41
|
+
|
42
|
+
controller = arguments[0]
|
43
|
+
mess = err.backtrace ? err.backtrace[0].gsub(/([\w -_]+):(\d+):in.*/,'\\1, Line \2: ') : ''
|
44
|
+
mess += err.message
|
45
|
+
controller.notify("description/error", :message => mess)
|
46
|
+
end# }}}
|
47
|
+
def self::inform_connectionwrapper_error(arguments,err) # {{{
|
48
|
+
controller = arguments[0]
|
49
|
+
puts err.message
|
50
|
+
puts err.backtrace
|
51
|
+
controller.notify("executionhandler/error", :message => err.backtrace[0].gsub(/([\w -_]+):(\d+):in.*/,'\\1, Line \2: ') + err.message)
|
52
|
+
end # }}}
|
53
|
+
def self::inform_position_change(arguments,ipc={}) # {{{
|
54
|
+
controller = arguments[0]
|
55
|
+
controller.notify("position/change", ipc)
|
56
|
+
end # }}}
|
57
|
+
|
58
|
+
def initialize(arguments,position=nil,continue=nil) # {{{
|
59
|
+
@controller = arguments[0]
|
60
|
+
@handler_continue = continue
|
61
|
+
@handler_position = position
|
62
|
+
@handler_passthrough = nil
|
63
|
+
@handler_returnValue = nil
|
64
|
+
@handler_returnOptions = nil
|
65
|
+
@handler_activity_uuid = Digest::MD5.hexdigest(Kernel::rand().to_s)
|
66
|
+
@label = ''
|
67
|
+
@guard_files = []
|
68
|
+
@guard_items = []
|
69
|
+
end # }}}
|
70
|
+
|
71
|
+
def prepare(readonly, endpoints, parameters) #{{{
|
72
|
+
@handler_endpoint = endpoints.is_a?(Array) ? endpoints.map{ |ep| readonly.endpoints[ep] }.compact : readonly.endpoints[endpoints]
|
73
|
+
if @controller.attributes['twin_engine']
|
74
|
+
@handler_endpoint_orig = @handler_endpoint
|
75
|
+
@handler_endpoint = @controller.attributes['twin_engine'].to_s + '?original_endpoint=' + Riddl::Protocols::Utils::escape(@handler_endpoint)
|
76
|
+
end
|
77
|
+
params = parameters.dup
|
78
|
+
params[:arguments] = params[:arguments].dup if params[:arguments]
|
79
|
+
params[:arguments]&.map! do |ele|
|
80
|
+
t = ele.dup
|
81
|
+
if t.value.is_a?(Proc)
|
82
|
+
t.value = readonly.instance_exec &t.value
|
83
|
+
end
|
84
|
+
t
|
85
|
+
end
|
86
|
+
params
|
87
|
+
end #}}}
|
88
|
+
|
89
|
+
def additional #{{{
|
90
|
+
{
|
91
|
+
:attributes => @controller.attributes,
|
92
|
+
:cpee => {
|
93
|
+
'base' => @controller.base_url,
|
94
|
+
'instance' => @controller.instance_id,
|
95
|
+
'instance_url' => @controller.instance_url,
|
96
|
+
'instance_uuid' => @controller.uuid
|
97
|
+
},
|
98
|
+
:task => {
|
99
|
+
'label' => @label,
|
100
|
+
'id' => @handler_position
|
101
|
+
}
|
102
|
+
}
|
103
|
+
end #}}}
|
104
|
+
|
105
|
+
def proto_curl(parameters) #{{{
|
106
|
+
params = []
|
107
|
+
callback = Digest::MD5.hexdigest(Kernel::rand().to_s)
|
108
|
+
(parameters[:arguments] || []).each do |s|
|
109
|
+
if s.respond_to?(:mimetype)
|
110
|
+
params << Riddl::Parameter::Complex.new(s.name.to_s,v.mimetype,v.value)
|
111
|
+
else
|
112
|
+
if s.name.to_s =~ /^_Q_/
|
113
|
+
params << Riddl::Parameter::Simple.new(s.name.to_s.sub(/^_Q_/,''),CPEE::ValueHelper::generate(s.value),:query)
|
114
|
+
elsif s.name.to_s =~ /^_B_/
|
115
|
+
params << Riddl::Parameter::Simple.new(s.name.to_s.sub(/^_B_/,''),CPEE::ValueHelper::generate(s.value),:body)
|
116
|
+
elsif s.name.to_s =~ /^_H_/
|
117
|
+
params << Riddl::Header.new(s.name.to_s.sub(/^_H_/,''),CPEE::ValueHelper::generate(s.value))
|
118
|
+
elsif s.name.to_s =~ /^_C_/
|
119
|
+
params << Riddl::Parameter::Complex.new(s.name.to_s.sub(/^_C_/,''),*CPEE::ValueHelper::generate(s.value).split(';',2))
|
120
|
+
else
|
121
|
+
params << Riddl::Parameter::Simple.new(s.name.to_s,CPEE::ValueHelper::generate(s.value))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
params << Riddl::Header.new("CPEE-BASE",@controller.base_url)
|
127
|
+
params << Riddl::Header.new("CPEE-INSTANCE",@controller.instance_id)
|
128
|
+
params << Riddl::Header.new("CPEE-INSTANCE-URL",@controller.instance_url)
|
129
|
+
params << Riddl::Header.new("CPEE-INSTANCE-UUID",@controller.uuid)
|
130
|
+
params << Riddl::Header.new("CPEE-CALLBACK",File.join(@controller.instance_url,'callbacks',callback,'/'))
|
131
|
+
params << Riddl::Header.new("CPEE-CALLBACK-ID",callback)
|
132
|
+
params << Riddl::Header.new("CPEE-ACTIVITY",@handler_position)
|
133
|
+
params << Riddl::Header.new("CPEE-LABEL",@label||'')
|
134
|
+
params << Riddl::Header.new("CPEE-TWIN-TARGET",@controller.attributes['twin_target']) if @controller.attributes['twin_target']
|
135
|
+
@controller.attributes.each do |key,value|
|
136
|
+
params << Riddl::Header.new("CPEE-ATTR-#{key.to_s.gsub(/_/,'-')}",value)
|
137
|
+
end
|
138
|
+
|
139
|
+
status = result = headers = nil
|
140
|
+
begin
|
141
|
+
tendpoint = @handler_endpoint.sub(/^http(s)?-(get|put|post|delete):/,'http\\1:')
|
142
|
+
type = $2 || parameters[:method] || 'post'
|
143
|
+
|
144
|
+
client = Riddl::Client.new(tendpoint)
|
145
|
+
|
146
|
+
@handler_passthrough = callback
|
147
|
+
@controller.callback(self,callback,:'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position)
|
148
|
+
|
149
|
+
status, result, headers = client.request type => params
|
150
|
+
@guard_files += result
|
151
|
+
|
152
|
+
if status == 561
|
153
|
+
if @controller.attributes['twin_translate']
|
154
|
+
gettrans = Riddl::Client.new(@controller.attributes['twin_translate'])
|
155
|
+
gtstatus, gtresult, gtheaders = gettrans.get
|
156
|
+
if gtstatus >= 200 && gtstatus < 300
|
157
|
+
transwhat = case headers['CPEE-TWIN-TASKTYPE']
|
158
|
+
when 'i'; 'instantiation'
|
159
|
+
when 'ir'; 'ipc-receive'
|
160
|
+
when 'is'; 'ipc-send'
|
161
|
+
else
|
162
|
+
'instantiation'
|
163
|
+
end
|
164
|
+
JSON::parse(gtresult.first.value.read).each do |e|
|
165
|
+
if e['type'] == transwhat
|
166
|
+
@handler_endpoint = e['endpoint'] if e['endpoint']
|
167
|
+
e['arguments']&.each do |k,a|
|
168
|
+
if a.is_a? String
|
169
|
+
hname = a.gsub(/-/,'_')
|
170
|
+
a = headers[hname] if headers[hname]
|
171
|
+
elsif a.is_a? Hash
|
172
|
+
a.each do |k_ht, a_ht|
|
173
|
+
hname = a_ht.gsub(/-/,'_')
|
174
|
+
a[k_ht] = headers[hname] if headers[hname]
|
175
|
+
end
|
176
|
+
end
|
177
|
+
params.each do |p|
|
178
|
+
if p.name == k
|
179
|
+
if a.is_a? String
|
180
|
+
p.value = a
|
181
|
+
elsif a.is_a? Hash
|
182
|
+
ohash = JSON::parse(p.value) rescue {}
|
183
|
+
ohash.merge!(a)
|
184
|
+
p.value = JSON.generate(ohash)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
else
|
193
|
+
@handler_endpoint = @handler_endpoint_orig
|
194
|
+
end
|
195
|
+
params.delete_if { |p| p.name == 'original_endpoint' }
|
196
|
+
end
|
197
|
+
end while status == 561
|
198
|
+
|
199
|
+
if status < 200 || status >= 300
|
200
|
+
headers['CPEE_SALVAGE'] = true
|
201
|
+
c = result[0]&.value
|
202
|
+
c = c.read if c.respond_to? :read
|
203
|
+
callback([ Riddl::Parameter::Complex.new('error','application/json',StringIO.new(JSON::generate({ 'status' => status, 'error' => c }))) ], headers)
|
204
|
+
else
|
205
|
+
if headers['CPEE_CALLBACK'] && headers['CPEE_CALLBACK'] == 'true' && result.any?
|
206
|
+
headers['CPEE_UPDATE'] = true
|
207
|
+
callback result, headers
|
208
|
+
elsif headers['CPEE_CALLBACK'] && headers['CPEE_CALLBACK'] == 'true' && result.empty?
|
209
|
+
if headers['CPEE_INSTANTIATION']
|
210
|
+
@controller.notify("task/instantiation", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :endpoint => @handler_endpoint, :received => CPEE::ValueHelper.parse(headers['CPEE_INSTANTIATION']))
|
211
|
+
end
|
212
|
+
if headers['CPEE_EVENT']
|
213
|
+
@controller.notify("task/#{headers['CPEE_EVENT'].gsub(/[^\w_-]/,'')}", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :endpoint => @handler_endpoint)
|
214
|
+
end
|
215
|
+
# do nothing, later on things will happend
|
216
|
+
else
|
217
|
+
callback result, headers
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end #}}}
|
221
|
+
|
222
|
+
def activity_handle(passthrough, parameters) # {{{
|
223
|
+
raise "Wrong endpoint" if @handler_endpoint.nil? || @handler_endpoint.empty?
|
224
|
+
@label = parameters[:label]
|
225
|
+
@anno = parameters.delete(:annotations) rescue nil
|
226
|
+
@controller.notify("status/resource_utilization", :mib => GetProcessMem.new.mb, **Process.times.to_h)
|
227
|
+
@controller.notify("activity/calling", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :passthrough => passthrough, :endpoint => @handler_endpoint, :parameters => parameters, :annotations => @anno)
|
228
|
+
if passthrough.to_s.empty?
|
229
|
+
proto_curl parameters
|
230
|
+
else
|
231
|
+
@controller.callback(self,passthrough,:'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position)
|
232
|
+
@handler_passthrough = passthrough
|
233
|
+
end
|
234
|
+
end # }}}
|
235
|
+
def activity_manipulate_handle(parameters) #{{{
|
236
|
+
@label = parameters[:label]
|
237
|
+
end #}}}
|
238
|
+
|
239
|
+
def activity_result_value # {{{
|
240
|
+
@handler_returnValue
|
241
|
+
end # }}}
|
242
|
+
def activity_result_options # {{{
|
243
|
+
@handler_returnOptions
|
244
|
+
end # }}}
|
245
|
+
|
246
|
+
def activity_stop # {{{
|
247
|
+
unless @handler_passthrough.nil?
|
248
|
+
@controller.cancel_callback(@handler_passthrough)
|
249
|
+
end
|
250
|
+
end # }}}
|
251
|
+
def activity_passthrough_value # {{{
|
252
|
+
@handler_passthrough
|
253
|
+
end # }}}
|
254
|
+
|
255
|
+
def activity_no_longer_necessary # {{{
|
256
|
+
true
|
257
|
+
end # }}}
|
258
|
+
|
259
|
+
def activity_uuid
|
260
|
+
@handler_activity_uuid
|
261
|
+
end
|
262
|
+
|
263
|
+
def inform_activity_done # {{{
|
264
|
+
@controller.notify("activity/done", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :label => @label, :activity => @handler_position)
|
265
|
+
@controller.notify("status/resource_utilization", :mib => GetProcessMem.new.mb, **Process.times.to_h)
|
266
|
+
end # }}}
|
267
|
+
def inform_activity_manipulate # {{{
|
268
|
+
@controller.notify("activity/manipulating", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :label => @label, :activity => @handler_position)
|
269
|
+
end # }}}
|
270
|
+
def inform_activity_failed(err) # {{{
|
271
|
+
puts err.message
|
272
|
+
puts err.backtrace
|
273
|
+
@controller.notify("activity/failed", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :label => @label, :activity => @handler_position, :message => err.message, :line => err.backtrace[0].match(/(.*?):(\d+):/)[2], :where => err.backtrace[0].match(/(.*?):(\d+):/)[1])
|
274
|
+
end # }}}
|
275
|
+
def inform_manipulate_change(status,changed_dataelements,changed_endpoints,dataelements,endpoints) # {{{
|
276
|
+
unless status.nil?
|
277
|
+
@controller.notify("status/change", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :label => @label, :activity => @handler_position, :id => status.id, :message => status.message)
|
278
|
+
end
|
279
|
+
unless changed_dataelements.nil? || changed_dataelements.empty?
|
280
|
+
de = dataelements.slice(*changed_dataelements).transform_values { |v| enc = CPEE::EvalRuby::Translation::detect_encoding(v); (enc == 'OTHER' ? v : (v.encode('UTF-8',enc) rescue CPEE::EvalRuby::Translation::convert_to_base64(v))) }
|
281
|
+
@controller.notify("dataelements/change", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :label => @label, :activity => @handler_position, :changed => changed_dataelements, :values => de)
|
282
|
+
end
|
283
|
+
unless changed_endpoints.nil? || changed_endpoints.empty?
|
284
|
+
@controller.notify("endpoints/change", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :label => @label, :activity => @handler_position, :changed => changed_endpoints, :values => endpoints.slice(*changed_endpoints))
|
285
|
+
end
|
286
|
+
end # }}}
|
287
|
+
|
288
|
+
def vote_sync_after # {{{
|
289
|
+
@controller.vote("activity/syncing_after", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :activity => @handler_position, :label => @label)
|
290
|
+
end # }}}
|
291
|
+
def vote_sync_before(parameters=nil) # {{{
|
292
|
+
@controller.vote("activity/syncing_before", :'activity-uuid' => @handler_activity_uuid, :endpoint => @handler_endpoint, :activity => @handler_position, :label => @label, :parameters => parameters)
|
293
|
+
end # }}}
|
294
|
+
|
295
|
+
def callback(result=nil,options={})
|
296
|
+
status, ret, headers = Riddl::Client.new(@controller.url_result_transformation).request 'put' => result
|
297
|
+
recv = if status >= 200 && status < 300
|
298
|
+
JSON::parse(ret[0].value.read)
|
299
|
+
else
|
300
|
+
nil
|
301
|
+
end
|
302
|
+
|
303
|
+
@controller.notify("activity/receiving", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :endpoint => @handler_endpoint, :received => recv, :annotations => @anno)
|
304
|
+
|
305
|
+
@guard_files += result
|
306
|
+
@guard_files += ret
|
307
|
+
|
308
|
+
if options['CPEE_INSTANTIATION']
|
309
|
+
@controller.notify("task/instantiation", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :endpoint => @handler_endpoint, :received => CPEE::ValueHelper.parse(options['CPEE_INSTANTIATION']))
|
310
|
+
end
|
311
|
+
if options['CPEE_EVENT']
|
312
|
+
@controller.notify("task/#{options['CPEE_EVENT'].gsub(/[^\w_-]/,'')}", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :endpoint => @handler_endpoint, :received => recv)
|
313
|
+
else
|
314
|
+
@handler_returnValue = recv
|
315
|
+
@handler_returnOptions = options
|
316
|
+
end
|
317
|
+
if options['CPEE_STATUS']
|
318
|
+
@controller.notify("activity/status", :'activity-uuid' => @handler_activity_uuid, :label => @label, :activity => @handler_position, :endpoint => @handler_endpoint, :status => options['CPEE_STATUS'])
|
319
|
+
end
|
320
|
+
if options['CPEE_UPDATE']
|
321
|
+
@handler_continue.continue WEEL::Signal::Again
|
322
|
+
else
|
323
|
+
@controller.cancel_callback(@handler_passthrough)
|
324
|
+
@handler_passthrough = nil
|
325
|
+
if options['CPEE_SALVAGE']
|
326
|
+
@handler_continue.continue WEEL::Signal::Salvage
|
327
|
+
elsif options['CPEE_STOP']
|
328
|
+
@handler_continue.continue WEEL::Signal::Stop
|
329
|
+
else
|
330
|
+
@handler_continue.continue
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
def mem_guard() #{{{
|
336
|
+
@guard_files.delete_if do |p|
|
337
|
+
if p&.respond_to?(:close)
|
338
|
+
p.close
|
339
|
+
elsif p&.value&.respond_to?(:close)
|
340
|
+
p.value.close
|
341
|
+
end
|
342
|
+
true
|
343
|
+
end
|
344
|
+
GC.start
|
345
|
+
end #}}}
|
346
|
+
|
347
|
+
def test_condition(dataelements,endpoints,local,additional,code,args={})
|
348
|
+
send = []
|
349
|
+
send.push Riddl::Parameter::Simple::new('code',code)
|
350
|
+
send.push Riddl::Parameter::Complex::new('dataelements','application/json', JSON::generate(dataelements))
|
351
|
+
send.push Riddl::Parameter::Complex::new('local','application/json', JSON::generate(local)) if local
|
352
|
+
send.push Riddl::Parameter::Complex::new('endpoints','application/json', JSON::generate(endpoints))
|
353
|
+
send.push Riddl::Parameter::Complex::new('additional','application/json', JSON::generate(additional))
|
354
|
+
|
355
|
+
status, ret, headers = Riddl::Client.new(@controller.url_code).request 'put' => send
|
356
|
+
recv = if status >= 200 && status < 300
|
357
|
+
ret[0].value
|
358
|
+
else
|
359
|
+
nil
|
360
|
+
end
|
361
|
+
recv && recv == "true" ? true : false
|
362
|
+
end
|
363
|
+
def eval_expression(dataelements,endpoints,local,additional,code)
|
364
|
+
send = []
|
365
|
+
send.push Riddl::Parameter::Simple::new('code',code)
|
366
|
+
send.push Riddl::Parameter::Complex::new('dataelements','application/json', JSON::generate(dataelements))
|
367
|
+
send.push Riddl::Parameter::Complex::new('local','application/json', JSON::generate(local)) if local
|
368
|
+
send.push Riddl::Parameter::Complex::new('endpoints','application/json', JSON::generate(endpoints))
|
369
|
+
send.push Riddl::Parameter::Complex::new('additional','application/json', JSON::generate(additional))
|
370
|
+
|
371
|
+
status, ret, headers = Riddl::Client.new(@controller.url_code).request 'put' => send
|
372
|
+
recv = if status >= 200 && status < 300
|
373
|
+
ret[0].value
|
374
|
+
else
|
375
|
+
nil
|
376
|
+
end
|
377
|
+
recv
|
378
|
+
end
|
379
|
+
def manipulate(readonly,lock,dataelements,endpoints,status,local,additional,code,where,result=nil,options=nil)
|
380
|
+
lock.synchronize do
|
381
|
+
send = []
|
382
|
+
send.push Riddl::Parameter::Simple::new('code',code)
|
383
|
+
send.push Riddl::Parameter::Complex::new('dataelements','application/json', JSON::generate(dataelements))
|
384
|
+
send.push Riddl::Parameter::Complex::new('local','application/json', JSON::generate(local)) if local
|
385
|
+
send.push Riddl::Parameter::Complex::new('endpoints','application/json', JSON::generate(endpoints))
|
386
|
+
send.push Riddl::Parameter::Complex::new('additional','application/json', JSON::generate(additional))
|
387
|
+
send.push Riddl::Parameter::Complex::new('status','application/json', JSON::generate(status)) if status
|
388
|
+
send.push Riddl::Parameter::Complex::new('call_result','application/json', JSON::generate(result))
|
389
|
+
send.push Riddl::Parameter::Complex::new('call_headers','application/json', JSON::generate(options))
|
390
|
+
|
391
|
+
stat, ret, headers = Riddl::Client.new(@controller.url_code).request 'put' => send
|
392
|
+
if stat >= 200 && stat < 300
|
393
|
+
ret.shift # drop result
|
394
|
+
signal = changed_status = nil
|
395
|
+
changed_dataelements = changed_local = changed_endpoints = []
|
396
|
+
signal = ret.shift.value if ret.any? && ret[0].name == 'signal'
|
397
|
+
changed_dataelements = JSON::parse(ret.shift.value.read) if ret.any? && ret[0].name == 'changed_dataelements'
|
398
|
+
changed_endpoints = JSON::parse(ret.shift.value.read) if ret.any? && ret[0].name == 'changed_endpoints'
|
399
|
+
changed_status = JSON::parse(ret.shift.value.read) if ret.any? && ret[0].name == 'changed_status'
|
400
|
+
|
401
|
+
struct = if readonly
|
402
|
+
WEEL::ReadStructure.new(dataelements,endpoints,local,additional)
|
403
|
+
else
|
404
|
+
WEEL::ManipulateStructure.new(dataelements, endpoints, status, local, additional)
|
405
|
+
end
|
406
|
+
struct.update(changed_dataelements,changed_endpoints,changed_status)
|
407
|
+
|
408
|
+
struct
|
409
|
+
else
|
410
|
+
nil
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def join_branches(branches) # factual, so for inclusive or [[a],[b],[c,d,e]]
|
416
|
+
@controller.notify("gateway/join", :instance_uuid => @controller.uuid, :branches => branches)
|
417
|
+
end
|
418
|
+
|
419
|
+
def simulate(type,nesting,tid,parent,parameters={}) #{{{
|
420
|
+
@controller.vote("simulating/step",
|
421
|
+
:'activity-uuid' => @handler_activity_uuid,
|
422
|
+
:label => @label,
|
423
|
+
:activity => tid,
|
424
|
+
:endpoint => @handler_endpoint,
|
425
|
+
:type => type,
|
426
|
+
:nesting => nesting,
|
427
|
+
:parent => parent,
|
428
|
+
:parameters => parameters
|
429
|
+
)
|
430
|
+
end #}}}
|
431
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# This file is part of CPEE.
|
2
|
+
#
|
3
|
+
# CPEE is free software: you can redistribute it and/or modify it under the terms
|
4
|
+
# of the GNU General Public License as published by the Free Software Foundation,
|
5
|
+
# either version 3 of the License, or (at your option) any later version.
|
6
|
+
#
|
7
|
+
# CPEE is distributed in the hope that it will be useful, but WITHOUT ANY
|
8
|
+
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
9
|
+
# PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
10
|
+
#
|
11
|
+
# You should have received a copy of the GNU General Public License along with
|
12
|
+
# CPEE (file COPYING in the main directory). If not, see
|
13
|
+
# <http://www.gnu.org/licenses/>.
|
14
|
+
|
15
|
+
require 'weel'
|
16
|
+
require 'json'
|
17
|
+
require 'redis'
|
18
|
+
require 'securerandom'
|
19
|
+
require 'riddl/client'
|
20
|
+
require 'cpee/value_helper'
|
21
|
+
require 'cpee/attributes_helper'
|
22
|
+
require 'cpee/message'
|
23
|
+
require 'cpee/redis'
|
24
|
+
require 'cpee/persistence'
|
25
|
+
|
26
|
+
require 'ostruct'
|
27
|
+
class ParaStruct < OpenStruct
|
28
|
+
def to_json(*a)
|
29
|
+
table.to_json
|
30
|
+
end
|
31
|
+
end
|
32
|
+
def ⭐(a); ParaStruct.new(a); end
|
33
|
+
|
34
|
+
class Controller
|
35
|
+
def initialize(id,dir,opts)
|
36
|
+
CPEE::redis_connect(opts,"Instance #{id}")
|
37
|
+
CPEE::Message::set_workers(opts[:workers])
|
38
|
+
|
39
|
+
@redis = opts[:redis]
|
40
|
+
@votes = []
|
41
|
+
|
42
|
+
@id = id
|
43
|
+
|
44
|
+
@attributes = {}
|
45
|
+
CPEE::Persistence::extract_list(id,opts,'attributes').each do |de|
|
46
|
+
@attributes[de[0]] = de[1]
|
47
|
+
end
|
48
|
+
|
49
|
+
@attributes_helper = AttributesHelper.new
|
50
|
+
@thread = nil
|
51
|
+
@opts = opts
|
52
|
+
@instance = nil
|
53
|
+
@loop_guard = {}
|
54
|
+
|
55
|
+
@callback_keys = {}
|
56
|
+
|
57
|
+
@subs = Thread.new do
|
58
|
+
@psredis = @opts[:redis_dyn].call "Instance #{@id} Callback Response"
|
59
|
+
@psredis.psubscribe('callback-response:*','callback-end:*') do |on|
|
60
|
+
on.pmessage do |pat, what, message|
|
61
|
+
if pat == 'callback-response:*'
|
62
|
+
_, worker, identifier = what.split(':')
|
63
|
+
if @callback_keys.has_key?(identifier)
|
64
|
+
index = message.index(' ')
|
65
|
+
mess = message[index+1..-1]
|
66
|
+
instance = message[0...index]
|
67
|
+
m = JSON.parse(mess)
|
68
|
+
resp = []
|
69
|
+
m['content']['values'].each do |e|
|
70
|
+
if e[1][0] == 'simple'
|
71
|
+
resp << Riddl::Parameter::Simple.new(e[0],e[1][1])
|
72
|
+
elsif e[1][0] == 'complex'
|
73
|
+
resp << Riddl::Parameter::Complex.new(e[0],e[1][1],File.open(e[1][2]))
|
74
|
+
end
|
75
|
+
end
|
76
|
+
@callback_keys[identifier].send(:callback,resp,m['content']['headers'])
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if pat == 'callback-end:*'
|
80
|
+
_, worker, identifier = what.split(':')
|
81
|
+
@callback_keys.delete(identifier)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@psredis.close
|
86
|
+
rescue => e
|
87
|
+
sleep 1
|
88
|
+
retry
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
attr_reader :id
|
93
|
+
attr_reader :attributes
|
94
|
+
attr_reader :loop_guard
|
95
|
+
|
96
|
+
def uuid
|
97
|
+
@attributes['uuid']
|
98
|
+
end
|
99
|
+
|
100
|
+
def attributes_translated
|
101
|
+
@attributes_helper.translate(attributes,dataelements,endpoints)
|
102
|
+
end
|
103
|
+
|
104
|
+
def host
|
105
|
+
@opts[:host]
|
106
|
+
end
|
107
|
+
def base_url
|
108
|
+
File.join(@opts[:url],'/')
|
109
|
+
end
|
110
|
+
def instance_url
|
111
|
+
File.join(@opts[:url].to_s,@id.to_s,'/')
|
112
|
+
end
|
113
|
+
def instance_id
|
114
|
+
@id
|
115
|
+
end
|
116
|
+
def base
|
117
|
+
base_url
|
118
|
+
end
|
119
|
+
def instance=(inst)
|
120
|
+
@instance = inst
|
121
|
+
end
|
122
|
+
def endpoints
|
123
|
+
@instance.endpoints
|
124
|
+
end
|
125
|
+
def dataelements
|
126
|
+
@instance.data
|
127
|
+
end
|
128
|
+
def url_result_transformation
|
129
|
+
@opts[:url_result_transformation]
|
130
|
+
end
|
131
|
+
def url_code
|
132
|
+
@opts[:url_code]
|
133
|
+
end
|
134
|
+
|
135
|
+
def start
|
136
|
+
if vote("state/change", :state => 'running')
|
137
|
+
@thread = @instance.start
|
138
|
+
@thread.join
|
139
|
+
else
|
140
|
+
@thread = @instance.stop
|
141
|
+
@thread.join
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def stop
|
146
|
+
### tell the instance to stop
|
147
|
+
@instance.stop
|
148
|
+
### end all votes or it will not work
|
149
|
+
Thread.new do # doing stuff in trap context is a nono. but in a thread its fine :-)
|
150
|
+
@votes.each do |key|
|
151
|
+
CPEE::Message::send(:'vote-response',key,base,@id,uuid,info,true,@redis)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def info
|
157
|
+
@attributes['info']
|
158
|
+
end
|
159
|
+
|
160
|
+
def notify(what,content={})
|
161
|
+
content[:attributes] = attributes_translated
|
162
|
+
CPEE::Message::send(:event,what,base,@id,uuid,info,content,@redis)
|
163
|
+
end
|
164
|
+
|
165
|
+
def vote(what,content={})
|
166
|
+
topic, name = what.split('/')
|
167
|
+
handler = File.join(topic,'vote',name)
|
168
|
+
votes = []
|
169
|
+
|
170
|
+
CPEE::Persistence::extract_handler(id,@opts,handler).each do |client|
|
171
|
+
voteid = Digest::MD5.hexdigest(Kernel::rand().to_s)
|
172
|
+
content[:key] = voteid
|
173
|
+
content[:attributes] = attributes_translated
|
174
|
+
content[:subscription] = client
|
175
|
+
votes << voteid
|
176
|
+
CPEE::Message::send(:vote,what,base,@id,uuid,info,content,@redis)
|
177
|
+
end
|
178
|
+
|
179
|
+
if votes.length > 0
|
180
|
+
@votes += votes
|
181
|
+
psredis = @opts[:redis_dyn].call "Instance #{@id} Vote"
|
182
|
+
collect = []
|
183
|
+
psredis.subscribe(votes.map{|e| ['vote-response:00:' + e.to_s] }.flatten) do |on|
|
184
|
+
on.message do |what, message|
|
185
|
+
index = message.index(' ')
|
186
|
+
mess = message[index+1..-1]
|
187
|
+
m = JSON.parse(mess)
|
188
|
+
collect << ((m['content'] == true || m['content'] == 'true') || false)
|
189
|
+
@votes.delete m['name']
|
190
|
+
cancel_callback m['name']
|
191
|
+
if collect.length >= votes.length
|
192
|
+
psredis.unsubscribe
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
!collect.include?(false)
|
197
|
+
else
|
198
|
+
true
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def callback(hw,key,content)
|
203
|
+
CPEE::Message::send(:callback,'activity/content',base,@id,uuid,info,content.merge(:key => key),@redis)
|
204
|
+
@callback_keys[key] = hw
|
205
|
+
end
|
206
|
+
|
207
|
+
def cancel_callback(key)
|
208
|
+
CPEE::Message::send(:'callback-end',key,base,@id,uuid,info,{},@redis)
|
209
|
+
end
|
210
|
+
end
|