aws-flow 1.0.0

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.
Files changed (62) hide show
  1. data/Gemfile +8 -0
  2. data/LICENSE.TXT +15 -0
  3. data/NOTICE.TXT +14 -0
  4. data/Rakefile +39 -0
  5. data/aws-flow-core/Gemfile +9 -0
  6. data/aws-flow-core/LICENSE.TXT +15 -0
  7. data/aws-flow-core/NOTICE.TXT +14 -0
  8. data/aws-flow-core/Rakefile +27 -0
  9. data/aws-flow-core/aws-flow-core.gemspec +12 -0
  10. data/aws-flow-core/lib/aws/flow.rb +26 -0
  11. data/aws-flow-core/lib/aws/flow/async_backtrace.rb +134 -0
  12. data/aws-flow-core/lib/aws/flow/async_scope.rb +195 -0
  13. data/aws-flow-core/lib/aws/flow/begin_rescue_ensure.rb +386 -0
  14. data/aws-flow-core/lib/aws/flow/fiber.rb +77 -0
  15. data/aws-flow-core/lib/aws/flow/flow_utils.rb +50 -0
  16. data/aws-flow-core/lib/aws/flow/future.rb +109 -0
  17. data/aws-flow-core/lib/aws/flow/implementation.rb +151 -0
  18. data/aws-flow-core/lib/aws/flow/simple_dfa.rb +85 -0
  19. data/aws-flow-core/lib/aws/flow/tasks.rb +405 -0
  20. data/aws-flow-core/test/aws/async_backtrace_spec.rb +41 -0
  21. data/aws-flow-core/test/aws/async_scope_spec.rb +118 -0
  22. data/aws-flow-core/test/aws/begin_rescue_ensure_spec.rb +665 -0
  23. data/aws-flow-core/test/aws/external_task_spec.rb +197 -0
  24. data/aws-flow-core/test/aws/factories.rb +52 -0
  25. data/aws-flow-core/test/aws/fiber_condition_variable_spec.rb +163 -0
  26. data/aws-flow-core/test/aws/fiber_spec.rb +78 -0
  27. data/aws-flow-core/test/aws/flow_spec.rb +255 -0
  28. data/aws-flow-core/test/aws/future_spec.rb +210 -0
  29. data/aws-flow-core/test/aws/rubyflow.rb +22 -0
  30. data/aws-flow-core/test/aws/simple_dfa_spec.rb +63 -0
  31. data/aws-flow-core/test/aws/spec_helper.rb +36 -0
  32. data/aws-flow.gemspec +13 -0
  33. data/lib/aws/decider.rb +67 -0
  34. data/lib/aws/decider/activity.rb +408 -0
  35. data/lib/aws/decider/activity_definition.rb +111 -0
  36. data/lib/aws/decider/async_decider.rb +673 -0
  37. data/lib/aws/decider/async_retrying_executor.rb +153 -0
  38. data/lib/aws/decider/data_converter.rb +40 -0
  39. data/lib/aws/decider/decider.rb +511 -0
  40. data/lib/aws/decider/decision_context.rb +60 -0
  41. data/lib/aws/decider/exceptions.rb +178 -0
  42. data/lib/aws/decider/executor.rb +149 -0
  43. data/lib/aws/decider/flow_defaults.rb +70 -0
  44. data/lib/aws/decider/generic_client.rb +178 -0
  45. data/lib/aws/decider/history_helper.rb +173 -0
  46. data/lib/aws/decider/implementation.rb +82 -0
  47. data/lib/aws/decider/options.rb +607 -0
  48. data/lib/aws/decider/state_machines.rb +373 -0
  49. data/lib/aws/decider/task_handler.rb +76 -0
  50. data/lib/aws/decider/task_poller.rb +207 -0
  51. data/lib/aws/decider/utilities.rb +187 -0
  52. data/lib/aws/decider/worker.rb +324 -0
  53. data/lib/aws/decider/workflow_client.rb +374 -0
  54. data/lib/aws/decider/workflow_clock.rb +104 -0
  55. data/lib/aws/decider/workflow_definition.rb +101 -0
  56. data/lib/aws/decider/workflow_definition_factory.rb +53 -0
  57. data/lib/aws/decider/workflow_enabled.rb +26 -0
  58. data/test/aws/decider_spec.rb +1299 -0
  59. data/test/aws/factories.rb +45 -0
  60. data/test/aws/integration_spec.rb +3108 -0
  61. data/test/aws/spec_helper.rb +23 -0
  62. metadata +138 -0
@@ -0,0 +1,373 @@
1
+ #--
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ #++
15
+
16
+ module AWS
17
+ module Flow
18
+
19
+ # @!visibility private
20
+ module DecisionStateMachineDFA
21
+ attr_accessor :transitions, :symbols, :states, :start_state
22
+
23
+ def init(start_state)
24
+ include InstanceMethods
25
+ @start_state = start_state
26
+ @symbols = []
27
+ @states = []
28
+ @transitions = {}
29
+ @states << start_state
30
+ end
31
+
32
+ def get_start_state
33
+ @start_state
34
+ end
35
+
36
+ def self_transitions(symbol)
37
+ states.each do |state|
38
+ add_transition(state, symbol, state) unless @transitions[[state, symbol]]
39
+ end
40
+ end
41
+
42
+ def get_transitions
43
+ # Turns out, you are your own ancestor
44
+ ancestors.slice(1..-1).map {|x| x.transitions if x.respond_to? :transitions}.compact.
45
+ inject({}) {|x, y| x.merge(y)}.merge(@transitions)
46
+ end
47
+
48
+ def add_transition(state, symbol, next_state, function = nil)
49
+ @symbols << symbol unless @symbols.include? symbol
50
+ [state, next_state].each do |this_state|
51
+ @states << this_state unless @states.include? this_state
52
+ end
53
+ @transitions[[state, symbol]] = [next_state, function]
54
+ end
55
+
56
+ def add_transitions(list_of_transitions)
57
+ list_of_transitions.each {|transition| add_transition(*transition)}
58
+ end
59
+
60
+ def uncovered_transitions
61
+ @states.product(@symbols) - @transitions.keys
62
+ end
63
+
64
+ module InstanceMethods
65
+ attr_accessor :current_state
66
+
67
+ def consume(symbol)
68
+ @state_history ||= [self.class.get_start_state]
69
+ @state_history << @current_state
70
+ @state_history << symbol
71
+ transition_tuple = self.class.get_transitions[[@current_state, symbol]]
72
+ raise "This is not a legal transition, attempting to consume #{symbol} while at state #{current_state}" unless transition_tuple
73
+ @current_state, func_to_call = transition_tuple
74
+ @state_history << @current_state
75
+ func_to_call.call(self) if func_to_call
76
+ end
77
+ end
78
+ end
79
+
80
+ # @!visibility private
81
+ class CompleteWorkflowStateMachine
82
+ extend DecisionStateMachineDFA
83
+ attr_reader :id
84
+ def consume(symbol)
85
+ return @decision = nil if symbol == :handle_initiation_failed_event
86
+ return if symbol == :handle_decision_task_started_event
87
+ raise "UnsupportedOperation"
88
+ end
89
+ # Creates a new CompleteWorkflowStateMachine
90
+ #
91
+ # @param id
92
+ # The decider id.
93
+ #
94
+ # @param attributes
95
+ #
96
+
97
+ def done?
98
+ ! @decision.nil?
99
+ end
100
+ def initialize(id, decision)
101
+ @id = id
102
+ @decision = decision
103
+ @current_state = :created
104
+ end
105
+ init(:created)
106
+
107
+ def get_decision
108
+ return @decision
109
+ end
110
+ end
111
+
112
+
113
+ # @!visibility private
114
+ class DecisionStateMachineBase
115
+ extend DecisionStateMachineDFA
116
+ attr_reader :id
117
+
118
+ def initialize(id)
119
+ @id = id
120
+ @current_state = :created
121
+ end
122
+
123
+
124
+ def handle_started_event(event)
125
+ @state_history << :handle_started_event
126
+ end
127
+
128
+ init(:created)
129
+
130
+ add_transitions [
131
+ [:cancellation_decision_sent, :handle_cancellation_event, :completed],
132
+ [:cancellation_decision_sent, :handle_cancellation_initiated_event, :cancellation_decision_sent],
133
+ [:cancellation_decision_sent, :handle_completion_event, :completed_after_cancellation_decision_sent],
134
+ [:cancelled_after_initiated, :handle_completion_event, :cancelled_after_initiated],
135
+ [:cancelled_before_initiated, :handle_initiated_event, :cancelled_after_initiated],
136
+ [:cancelled_before_initiated, :handle_initiation_failed_event, :completed],
137
+ [:completed_after_cancellation_decision_sent, :handle_cancellation_failure_event, :completed],
138
+ [:created, :cancel, :completed, lambda { |immediate_cancellation_callback| immediate_cancellation_callback.run }],
139
+ [:created, :handle_decision_task_started_event, :decision_sent],
140
+ [:decision_sent, :cancel, :cancelled_before_initiated],
141
+ [:decision_sent, :handle_initiated_event, :initiated],
142
+ [:decision_sent, :handle_initiation_failed_event, :decision_sent],
143
+ [:initiated, :cancel, :cancelled_after_initiated],
144
+ [:initiated, :handle_completion_event, :completed],
145
+ [:started, :handle_decision_task_started_event, :started],
146
+ ]
147
+ self_transitions(:handle_decision_task_started_event)
148
+
149
+ def done?
150
+ @current_state == :completed || @current_state == :completed_after_cancellation_decision_sent
151
+ end
152
+ end
153
+
154
+
155
+ # @!visibility private
156
+ class ActivityDecisionStateMachine < DecisionStateMachineBase
157
+
158
+ attr_reader :attributes
159
+ # Creates a new `ActivityDecisionStateMachine`.
160
+ #
161
+ # @param [DecisionID] decision_id
162
+ #
163
+ # @param attributes
164
+ #
165
+ def initialize(decision_id, attributes)
166
+ @attributes = attributes
167
+ super(decision_id)
168
+ end
169
+ init(:created)
170
+ add_transitions [
171
+ [:cancelled_after_initiated, :handle_decision_task_started_event, :cancellation_decision_sent],
172
+ [:cancellation_decision_sent, :handle_cancellation_failure_event, :initiated]
173
+ ]
174
+ def get_decision
175
+ case @current_state
176
+ when :created
177
+ return create_schedule_activity_task_decision
178
+ when :cancelled_after_initiated
179
+ return create_request_cancel_activity_task_decision
180
+ end
181
+ end
182
+
183
+ def create_schedule_activity_task_decision
184
+ options = @attributes[:options]
185
+ attribute_type = :schedule_activity_task_decision_attributes
186
+ result = { :decision_type => "ScheduleActivityTask",
187
+ attribute_type =>
188
+ {
189
+ :activity_type =>
190
+ {
191
+ :name => @attributes[:activity_type].name.to_s,
192
+ :version => options.version.to_s
193
+ },
194
+ :activity_id => @attributes[:decision_id].to_s,
195
+ }
196
+ }
197
+ task_list = options.task_list ? {:task_list => {:name => options.task_list}} : {}
198
+ to_add = options.get_options([:heartbeat_timeout, :schedule_to_close_timeout, :schedule_to_start_timeout, :start_to_close_timeout, :input], task_list)
199
+ result[attribute_type].merge!(to_add)
200
+ result
201
+ end
202
+
203
+ def create_request_cancel_activity_task_decision
204
+ { :decision_type => "RequestCancelActivityTask",
205
+ :request_cancel_activity_task_decision_attributes => {:activity_id => @attributes[:decision_id]} }
206
+ end
207
+ end
208
+
209
+
210
+ # @!visibility private
211
+ class TimerDecisionStateMachine < DecisionStateMachineBase
212
+ attr_accessor :cancelled
213
+ def initialize(decision_id, attributes)
214
+ @attributes = attributes
215
+ super(decision_id)
216
+ end
217
+
218
+ def create_start_timer_decision
219
+ {
220
+ :decision_type => "StartTimer",
221
+ :start_timer_decision_attributes =>
222
+ {
223
+ :timer_id => @attributes[:timer_id].to_s,
224
+ # TODO find out what the "control" field is, and what it is for
225
+ :start_to_fire_timeout => @attributes[:start_to_fire_timeout]
226
+ }
227
+ }
228
+ end
229
+
230
+ def create_cancel_timer_decision
231
+ {
232
+ :decision_type => "CancelTimer",
233
+ :cancel_timer_decision_attributes => {
234
+ :timer_id => @attributes[:timer_id].to_s,
235
+ }
236
+ }
237
+ end
238
+
239
+ def get_decision
240
+ case @current_state
241
+ when :created
242
+ return create_start_timer_decision
243
+ when :cancelled_after_initiated
244
+ return create_cancel_timer_decision
245
+ end
246
+ end
247
+
248
+ def done?
249
+ @current_state == :completed || @cancelled
250
+ end
251
+
252
+ init(:created)
253
+ add_transitions [
254
+ [:cancelled_after_initiated, :handle_decision_task_started_event, :cancellation_decision_sent],
255
+ [:cancellation_decision_sent, :handle_cancellation_failure_event, :initiated],
256
+ ]
257
+ end
258
+
259
+
260
+ # @!visibility private
261
+ class SignalDecisionStateMachine < DecisionStateMachineBase
262
+ def initialize(decision_id, attributes)
263
+ @attributes = attributes
264
+ super(decision_id)
265
+ end
266
+
267
+ def get_decision
268
+ case @current_state
269
+ when :created
270
+ return create_signal_external_workflow_execution_decison
271
+ end
272
+ end
273
+
274
+ def create_signal_external_workflow_execution_decison
275
+ extra_options = {}
276
+ [:input, :control, :run_id].each do |type|
277
+ extra_options[type] = @attributes.send(type) if @attributes.send(type)
278
+ end
279
+ result = {
280
+ :decision_type => "SignalExternalWorkflowExecution",
281
+ :signal_external_workflow_execution_decision_attributes =>
282
+ {
283
+ :signal_name => @attributes.signal_name,
284
+ :workflow_id => @attributes.workflow_id
285
+ }
286
+ }
287
+ if ! extra_options.empty?
288
+ result[:signal_external_workflow_execution_decision_attributes].merge! extra_options
289
+ end
290
+ result
291
+ end
292
+ init(:created)
293
+ add_transitions [
294
+ [:created, :handle_decision_task_started_event, :decision_sent],
295
+ [:created, :cancel, :created],
296
+ [:initiated, :cancel, :completed, lambda {|immediate_cancellation_callback| immediate_cancellation_callback.run }],
297
+ [:decision_sent, :handle_initiated_event, :initiated],
298
+ [:cancelled_before_initiated, :handle_initiated_event, :cancelled_before_initiated],
299
+ [:decision_sent, :handle_completion_event, :completed],
300
+ [:initiated, :handle_completion_event, :completed],
301
+ [:cancelled_before_initiated, :handle_completion_event, :completed],
302
+ [:completed, :handle_completion_event, :completed]
303
+ ]
304
+
305
+ end
306
+
307
+
308
+ # @!visibility private
309
+ class ChildWorkflowDecisionStateMachine < DecisionStateMachineBase
310
+ attr_accessor :run_id, :attributes
311
+ def initialize(decision_id, attributes)
312
+ @attributes = attributes
313
+ super(decision_id)
314
+ end
315
+
316
+ def create_start_child_workflow_execution_decision
317
+ options = @attributes[:options]
318
+ workflow_name = options.workflow_name || options.prefix_name
319
+ attribute_name = :start_child_workflow_execution_decision_attributes
320
+ result = {
321
+ :decision_type => "StartChildWorkflowExecution",
322
+ attribute_name =>
323
+ {
324
+ :workflow_type =>
325
+ {
326
+ :name => "#{workflow_name}.#{options.execution_method}",
327
+ :version => options.version
328
+ },
329
+ :workflow_id => @attributes[:workflow_id].to_s,
330
+ :task_list => {
331
+ :name => options.task_list
332
+ },
333
+ # :control => @attributes[:control]
334
+ :tag_list => @attributes[:tag_list]
335
+ }
336
+ }
337
+ #TODO Figure out what control is
338
+ to_add = options.get_options([:execution_start_to_close_timeout, :task_start_to_close_timeout, :child_policy, :tag_list, :input])
339
+ result[attribute_name].merge!(to_add)
340
+ result
341
+ end
342
+
343
+ def create_request_cancel_external_workflow_execution_decision
344
+ result = {
345
+ :decision_type => "RequestCancelExternalWorkflowExecution",
346
+ :request_cancel_external_workflow_execution_decision_attributes => {
347
+ :workflow_id => @attributes[:workflow_id].to_s,
348
+ :run_id => @run_id.to_s,
349
+ }
350
+ }
351
+ end
352
+
353
+ def get_decision
354
+ case @current_state
355
+ when :created
356
+ return create_start_child_workflow_execution_decision
357
+ when :cancelled_after_started
358
+ return create_request_cancel_external_workflow_execution_decision
359
+ end
360
+ end
361
+ init(:created)
362
+ add_transitions [
363
+ [:cancelled_after_started, :handle_decision_task_started_event, :cancellation_decision_sent],
364
+ [:initiated, :handle_started_event, :started],
365
+ [:cancelled_after_initiated, :handle_started_event, :cancelled_after_started],
366
+ [:cancellation_decision_sent, :handle_cancellation_failure_event, :started],
367
+ [:started, :cancel, :cancelled_after_started],
368
+ [:started, :handle_completion_event, :completed],
369
+ [:cancelled_after_started, :handle_completion_event, :completed],
370
+ ]
371
+ end
372
+ end
373
+ end
@@ -0,0 +1,76 @@
1
+ #--
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ #++
15
+
16
+ module AWS
17
+ module Flow
18
+
19
+
20
+ # A decision task handler to work with a {WorkflowTaskPoller}. Create a DecisionTaskHandler and pass it to
21
+ # WorkflowTaskPoller on {WorkflowTaskPoller#initialize construction}.
22
+ class DecisionTaskHandler
23
+
24
+ # Creates a new DecisionTaskHandler
25
+ #
26
+ # @param workflow_definition_map
27
+ #
28
+ # @param options
29
+ # An optional logger.
30
+ #
31
+ def initialize(workflow_definition_map, options=nil)
32
+ @workflow_definition_map = workflow_definition_map
33
+ @logger = options.logger if options
34
+ @logger ||= Utilities::LogFactory.make_logger(self, "debug")
35
+ end
36
+
37
+
38
+ # Handles a decision task
39
+ #
40
+ # @param decision_task_iterator
41
+ #
42
+ def handle_decision_task(decision_task_iterator)
43
+ history_helper = HistoryHelper.new(decision_task_iterator)
44
+ @logger.debug "made history_helper"
45
+ decider = create_async_decider(history_helper)
46
+ @logger.debug "made async_decider"
47
+ decider.decide
48
+ @logger.debug "decided"
49
+ decisions = decider.get_decisions
50
+ response = {:task_token => decider.task_token}
51
+ context_data = decider.decision_helper.workflow_context_data
52
+ response[:execution_context] = context_data.to_s unless context_data.nil?
53
+ response[:decisions] = decisions unless decisions.nil?
54
+ return response
55
+ end
56
+
57
+ # Creates a new asynchronous decider.
58
+ #
59
+ # @param history_helper
60
+ #
61
+ # @return [AsyncDecider] the created AsyncDecider.
62
+ #
63
+ def create_async_decider(history_helper)
64
+ task = history_helper.get_decision_task
65
+ workflow_type = task.workflow_type
66
+ # TODO put in context correctly
67
+ workflow_definition_factory = @workflow_definition_map[workflow_type]
68
+ raise "No such definition for #{workflow_type}" if workflow_definition_factory.nil?
69
+ AsyncDecider.new(workflow_definition_factory, history_helper, DecisionHelper.new)
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
76
+ end
@@ -0,0 +1,207 @@
1
+ #--
2
+ # Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License").
5
+ # You may not use this file except in compliance with the License.
6
+ # A copy of the License is located at
7
+ #
8
+ # http://aws.amazon.com/apache2.0
9
+ #
10
+ # or in the "license" file accompanying this file. This file is distributed
11
+ # on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12
+ # express or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ #++
15
+
16
+ require 'tmpdir'
17
+
18
+ module AWS
19
+ module Flow
20
+
21
+ class WorkflowTaskPoller
22
+
23
+
24
+ # Creates a new WorkflowTaskPoller
25
+ #
26
+ # @param service
27
+ # The Amazon SWF service object on which this task poller will operate.
28
+ #
29
+ # @param [String] domain
30
+ # The name of the domain containing the task lists to poll.
31
+ #
32
+ # @param [DecisionTaskHandler] handler
33
+ # A {DecisionTaskHandler} to handle polled tasks. The poller will call the {DecisionTaskHandler#handle_decision_task} method.
34
+ #
35
+ # @param [Array] task_list
36
+ # Specifies the task list to poll for decision tasks.
37
+ #
38
+ # @param [Object] options
39
+ # Options to use for the logger.
40
+ #
41
+ def initialize(service, domain, handler, task_list, options=nil)
42
+ @service = service
43
+ @handler = handler
44
+ @domain = domain
45
+ @task_list = task_list
46
+ @logger = options.logger if options
47
+ @logger ||= Utilities::LogFactory.make_logger(self, "debug")
48
+ end
49
+
50
+
51
+
52
+ # Retrieves any decision tasks that are ready.
53
+ def get_decision_tasks
54
+ @domain.decision_tasks.poll_for_single_task(@task_list)
55
+ end
56
+
57
+ def poll_and_process_single_task
58
+ # TODO waitIfSuspended
59
+ begin
60
+ @logger.debug "Starting a new task...\n\n\n"
61
+ tasks = get_decision_tasks
62
+ return false if tasks.nil?
63
+ @logger.debug "We have this many tasks #{tasks}"
64
+ @logger.debug "debugging on #{tasks}\n"
65
+ task_completed_request = @handler.handle_decision_task(tasks)
66
+ @logger.debug "task to be responded to with #{task_completed_request}\n"
67
+ if !task_completed_request[:decisions].empty? && (task_completed_request[:decisions].first.keys.include?(:fail_workflow_execution_decision_attributes))
68
+ fail_hash = task_completed_request[:decisions].first[:fail_workflow_execution_decision_attributes]
69
+ reason = fail_hash[:reason]
70
+ details = fail_hash[:details]
71
+ @logger.debug "#{reason}, #{details}"
72
+ end
73
+ @service.respond_decision_task_completed(task_completed_request)
74
+ rescue AWS::SimpleWorkflow::Errors::UnknownResourceFault => e
75
+ # Log stuff
76
+ @logger.debug "Error in the poller, #{e}"
77
+ @logger.debug "The error class in #{e.class}"
78
+ end
79
+ end
80
+ end
81
+
82
+ class ActivityTaskPoller
83
+ def initialize(service, domain, task_list, activity_definition_map, options=nil)
84
+ @service = service
85
+ @domain = domain
86
+ @task_list = task_list
87
+ @activity_definition_map = activity_definition_map
88
+ @logger = options.logger if options
89
+ @logger ||= Utilities::LogFactory.make_logger(self, "debug")
90
+ max_workers = options.execution_workers if options
91
+ max_workers = 20 if (max_workers.nil? || max_workers.zero?)
92
+ @executor = ForkingExecutor.new(:max_workers => max_workers, :logger => @logger)
93
+
94
+ end
95
+
96
+ def execute(task)
97
+ activity_type = task.activity_type
98
+ begin
99
+ context = ActivityExecutionContext.new(@service, @domain, task)
100
+ activity_implementation = @activity_definition_map[activity_type]
101
+ raise "This activity worker was told to work on activity type #{activity_type.name}, but this activity worker only knows how to work on #{@activity_definition_map.keys.map(&:name).join' '}" unless activity_implementation
102
+ output = activity_implementation.execute(task.input, context)
103
+ @logger.debug "Responding on task_token #{task.task_token} for task #{task}"
104
+ if ! activity_implementation.execution_options.manual_completion
105
+ @service.respond_activity_task_completed(:task_token => task.task_token, :result => output)
106
+ end
107
+ rescue ActivityFailureException => e
108
+ respond_activity_task_failed_with_retry(task.task_token, e.message, e.details)
109
+ end
110
+ #TODO all the completion stuffs
111
+ end
112
+
113
+ def respond_activity_task_failed_with_retry(task_token, reason, details)
114
+ #TODO Set up this variable
115
+ if @failure_retrier.nil?
116
+ respond_activity_task_failed(task_token, reason, details)
117
+ #TODO Set up other stuff to do if we have it
118
+ end
119
+ end
120
+
121
+ def respond_activity_task_canceled_with_retry(task_token, message)
122
+ if @failure_retrier.nil?
123
+ respond_activity_task_canceled(task_token, message)
124
+ end
125
+ #TODO Set up other stuff to do if we have it
126
+ end
127
+
128
+ def respond_activity_task_canceled(task_token, message)
129
+ @service.respond_activity_task_canceled({:task_token => task_token, :details => message})
130
+ end
131
+
132
+ def respond_activity_task_failed(task_token, reason, details)
133
+ @logger.debug "The task token to be reported on is #{task_token}"
134
+ @service.respond_activity_task_failed(:task_token => task_token, :reason => reason.to_s, :details => details.to_s)
135
+ end
136
+
137
+ def process_single_task(task)
138
+ @service = AWS::SimpleWorkflow.new.client.with_http_handler(AWS::Core::Http::NetHttpHandler.new(:ssl_ca_file => AWS.config.ssl_ca_file))
139
+ begin
140
+ begin
141
+ execute(task)
142
+ rescue CancellationException => e
143
+ respond_activity_task_canceled_with_retry(task.task_token, e.message)
144
+ rescue Exception => e
145
+ @logger.error "Got an error, #{e.message}, while executing #{task.activity_type.name}"
146
+ @logger.error "Full stack trace: #{e.backtrace}"
147
+ respond_activity_task_failed_with_retry(task.task_token, e.message, e.backtrace)
148
+ #Do rescue stuff
149
+ ensure
150
+ @poll_semaphore.release
151
+ end
152
+ rescue Exception => e
153
+ semaphore_needs_release = true
154
+ @logger.debug "Got into the other error mode"
155
+ raise e
156
+ ensure
157
+ @poll_semaphore.release if semaphore_needs_release
158
+ end
159
+ end
160
+
161
+ def poll_and_process_single_task(use_forking = true)
162
+
163
+ @poll_semaphore ||= SuspendableSemaphore.new
164
+ @poll_semaphore.acquire
165
+ semaphore_needs_release = true
166
+ @logger.debug "before the poll\n\n"
167
+ begin
168
+ task = @domain.activity_tasks.poll_for_single_task(@task_list)
169
+ @logger.error "got a task, #{task.activity_type.name}"
170
+ @logger.error "The task token I got was: #{task.task_token}"
171
+ rescue Exception => e
172
+ @logger.debug "I have not been able to poll successfully, and am now bailing out, with error #{e}"
173
+ @poll_semaphore.release
174
+ return false
175
+ end
176
+ if task.nil?
177
+ "Still polling at #{Time.now}, but didn't get anything"
178
+ @logger.debug "Still polling at #{Time.now}, but didn't get anything"
179
+ @poll_semaphore.release
180
+ return false
181
+ end
182
+ semaphore_needs_release = false
183
+ if use_forking
184
+ @executor.execute { process_single_task(task) }
185
+ else
186
+ process_single_task(task)
187
+ end
188
+ # process_single_task(task)
189
+ @logger.debug "finished executing the task"
190
+ return true
191
+ end
192
+ end
193
+
194
+ class SuspendableSemaphore
195
+
196
+ def initialize
197
+
198
+ end
199
+
200
+ def acquire
201
+ end
202
+
203
+ def release
204
+ end
205
+ end
206
+ end
207
+ end