qwirk 0.0.1
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/History.md +7 -0
- data/LICENSE.txt +201 -0
- data/README.md +180 -0
- data/Rakefile +34 -0
- data/examples/README +1 -0
- data/examples/activemq.xml +84 -0
- data/examples/advanced_requestor/README.md +15 -0
- data/examples/advanced_requestor/base_request_worker.rb +18 -0
- data/examples/advanced_requestor/char_count_worker.rb +16 -0
- data/examples/advanced_requestor/config.ru +24 -0
- data/examples/advanced_requestor/exception_raiser_worker.rb +17 -0
- data/examples/advanced_requestor/length_worker.rb +14 -0
- data/examples/advanced_requestor/print_worker.rb +14 -0
- data/examples/advanced_requestor/publisher.rb +49 -0
- data/examples/advanced_requestor/qwirk.yml +16 -0
- data/examples/advanced_requestor/reverse_worker.rb +14 -0
- data/examples/advanced_requestor/triple_worker.rb +14 -0
- data/examples/batch/my_batch_worker.rb +30 -0
- data/examples/batch/my_line_worker.rb +8 -0
- data/examples/qwirk.yml +20 -0
- data/examples/requestor/README.md +13 -0
- data/examples/requestor/config.ru +13 -0
- data/examples/requestor/qwirk_persist.yml +5 -0
- data/examples/requestor/requestor.rb +68 -0
- data/examples/requestor/reverse_echo_worker.rb +15 -0
- data/examples/setup.rb +13 -0
- data/examples/shared/README.md +24 -0
- data/examples/shared/config.ru +13 -0
- data/examples/shared/publisher.rb +49 -0
- data/examples/shared/qwirk_persist.yml +5 -0
- data/examples/shared/shared_worker.rb +16 -0
- data/examples/simple/README +53 -0
- data/examples/simple/bar_worker.rb +10 -0
- data/examples/simple/baz_worker.rb +10 -0
- data/examples/simple/config.ru +14 -0
- data/examples/simple/publisher.rb +49 -0
- data/examples/simple/qwirk_persist.yml +4 -0
- data/examples/simple/tmp/kahadb/db-1.log +0 -0
- data/examples/simple/tmp/kahadb/db.data +0 -0
- data/examples/simple/tmp/kahadb/db.redo +0 -0
- data/examples/task/README +47 -0
- data/examples/task/config.ru +14 -0
- data/examples/task/foo_worker.rb +10 -0
- data/examples/task/messages.out +1000 -0
- data/examples/task/publisher.rb +25 -0
- data/examples/task/qwirk_persist.yml +5 -0
- data/examples/task/task.rb +36 -0
- data/lib/qwirk.rb +63 -0
- data/lib/qwirk/adapter.rb +45 -0
- data/lib/qwirk/base_worker.rb +96 -0
- data/lib/qwirk/batch.rb +4 -0
- data/lib/qwirk/batch/acquire_file_strategy.rb +47 -0
- data/lib/qwirk/batch/active_record.rb +3 -0
- data/lib/qwirk/batch/active_record/batch_job.rb +111 -0
- data/lib/qwirk/batch/active_record/failed_record.rb +5 -0
- data/lib/qwirk/batch/active_record/outstanding_record.rb +6 -0
- data/lib/qwirk/batch/file_status_strategy.rb +86 -0
- data/lib/qwirk/batch/file_worker.rb +228 -0
- data/lib/qwirk/batch/job_status.rb +29 -0
- data/lib/qwirk/batch/parse_file_strategy.rb +48 -0
- data/lib/qwirk/engine.rb +9 -0
- data/lib/qwirk/loggable.rb +23 -0
- data/lib/qwirk/manager.rb +140 -0
- data/lib/qwirk/marshal_strategy.rb +74 -0
- data/lib/qwirk/marshal_strategy/bson.rb +37 -0
- data/lib/qwirk/marshal_strategy/json.rb +37 -0
- data/lib/qwirk/marshal_strategy/none.rb +26 -0
- data/lib/qwirk/marshal_strategy/ruby.rb +26 -0
- data/lib/qwirk/marshal_strategy/string.rb +25 -0
- data/lib/qwirk/marshal_strategy/yaml.rb +25 -0
- data/lib/qwirk/publish_handle.rb +170 -0
- data/lib/qwirk/publisher.rb +67 -0
- data/lib/qwirk/queue_adapter.rb +3 -0
- data/lib/qwirk/queue_adapter/active_mq.rb +13 -0
- data/lib/qwirk/queue_adapter/active_mq/publisher.rb +12 -0
- data/lib/qwirk/queue_adapter/active_mq/worker_config.rb +16 -0
- data/lib/qwirk/queue_adapter/in_mem.rb +7 -0
- data/lib/qwirk/queue_adapter/in_mem/factory.rb +45 -0
- data/lib/qwirk/queue_adapter/in_mem/publisher.rb +98 -0
- data/lib/qwirk/queue_adapter/in_mem/queue.rb +88 -0
- data/lib/qwirk/queue_adapter/in_mem/reply_queue.rb +56 -0
- data/lib/qwirk/queue_adapter/in_mem/topic.rb +48 -0
- data/lib/qwirk/queue_adapter/in_mem/worker.rb +63 -0
- data/lib/qwirk/queue_adapter/in_mem/worker_config.rb +59 -0
- data/lib/qwirk/queue_adapter/jms.rb +50 -0
- data/lib/qwirk/queue_adapter/jms/connection.rb +42 -0
- data/lib/qwirk/queue_adapter/jms/consumer.rb +37 -0
- data/lib/qwirk/queue_adapter/jms/publisher.rb +126 -0
- data/lib/qwirk/queue_adapter/jms/worker.rb +89 -0
- data/lib/qwirk/queue_adapter/jms/worker_config.rb +38 -0
- data/lib/qwirk/remote_exception.rb +42 -0
- data/lib/qwirk/request_worker.rb +62 -0
- data/lib/qwirk/task.rb +177 -0
- data/lib/qwirk/task.rb.sav +194 -0
- data/lib/qwirk/version.rb +3 -0
- data/lib/qwirk/worker.rb +222 -0
- data/lib/qwirk/worker_config.rb +187 -0
- data/lib/rails/generators/qwirk/qwirk_generator.rb +82 -0
- data/lib/rails/generators/qwirk/templates/initializer.rb +6 -0
- data/lib/rails/generators/qwirk/templates/migration.rb +9 -0
- data/lib/rails/generators/qwirk/templates/schema.rb +28 -0
- data/lib/rails/railties/tasks.rake +8 -0
- data/lib/tasks/qwirk_tasks.rake +4 -0
- data/test/base_test.rb +248 -0
- data/test/database.yml +14 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +45 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +22 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +26 -0
- data/test/dummy/config/environments/production.rb +49 -0
- data/test/dummy/config/environments/test.rb +35 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/log/development.log +0 -0
- data/test/dummy/log/production.log +0 -0
- data/test/dummy/log/server.log +0 -0
- data/test/dummy/log/test.log +0 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/public/javascripts/application.js +2 -0
- data/test/dummy/public/javascripts/controls.js +965 -0
- data/test/dummy/public/javascripts/dragdrop.js +974 -0
- data/test/dummy/public/javascripts/effects.js +1123 -0
- data/test/dummy/public/javascripts/prototype.js +6001 -0
- data/test/dummy/public/javascripts/rails.js +191 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +7 -0
- data/test/jms.yml +9 -0
- data/test/jms_fail_test.rb +149 -0
- data/test/jms_requestor_block_test.rb +278 -0
- data/test/jms_requestor_test.rb +238 -0
- data/test/jms_test.rb +287 -0
- data/test/marshal_strategy_test.rb +62 -0
- data/test/support/integration_case.rb +5 -0
- data/test/test_helper.rb +7 -0
- data/test/test_helper.rbold +22 -0
- data/test/test_helper_active_record.rb +61 -0
- data/test/unit/qwirk/batch/acquire_file_strategy_test.rb +101 -0
- data/test/unit/qwirk/batch/active_record/batch_job_test.rb +35 -0
- data/test/unit/qwirk/batch/parse_file_strategy_test.rb +49 -0
- metadata +366 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module QueueAdapter
|
|
3
|
+
module ActiveMQ
|
|
4
|
+
class Publisher < JMS::Publisher
|
|
5
|
+
def initialize(queue_adapter, queue_name, topic_name, options, response_options)
|
|
6
|
+
topic_name = "VirtualTopic.#{topic_name}" if topic_name
|
|
7
|
+
super(queue_adapter, queue_name, topic_name, options, response_options)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Handle Messaging and Queuing using ActiveMQ
|
|
2
|
+
module Qwirk
|
|
3
|
+
module QueueAdapter
|
|
4
|
+
module ActiveMQ
|
|
5
|
+
class WorkerConfig < JMS::WorkerConfig
|
|
6
|
+
def initialize(queue_adapter, parent, queue_name, topic_name, options, response_options)
|
|
7
|
+
if topic_name
|
|
8
|
+
queue_name = "Consumer.#{parent.name}.VirtualTopic.#{topic_name}"
|
|
9
|
+
topic_name = nil
|
|
10
|
+
end
|
|
11
|
+
super(queue_adapter, parent, queue_name, topic_name, options, response_options)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
require 'qwirk/queue_adapter/in_mem/factory'
|
|
2
|
+
require 'qwirk/queue_adapter/in_mem/publisher'
|
|
3
|
+
require 'qwirk/queue_adapter/in_mem/queue'
|
|
4
|
+
require 'qwirk/queue_adapter/in_mem/reply_queue'
|
|
5
|
+
require 'qwirk/queue_adapter/in_mem/topic'
|
|
6
|
+
require 'qwirk/queue_adapter/in_mem/worker_config'
|
|
7
|
+
require 'qwirk/queue_adapter/in_mem/worker'
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module QueueAdapter
|
|
3
|
+
module InMem
|
|
4
|
+
module Factory
|
|
5
|
+
class << self
|
|
6
|
+
def init
|
|
7
|
+
@queue_hash = {}
|
|
8
|
+
@topic_hash = {}
|
|
9
|
+
@queue_hash_mutex = Mutex.new
|
|
10
|
+
@topic_hash_mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_worker_queue(worker_name, queue_name, topic_name, queue_max_size)
|
|
14
|
+
if queue_name
|
|
15
|
+
@queue_hash_mutex.synchronize do
|
|
16
|
+
queue = @queue_hash[queue_name] ||= Queue.new(queue_name)
|
|
17
|
+
queue.max_size = queue_max_size
|
|
18
|
+
return queue
|
|
19
|
+
end
|
|
20
|
+
else
|
|
21
|
+
@topic_hash_mutex.synchronize do
|
|
22
|
+
topic = @topic_hash[topic_name] ||= Topic.new(topic_name)
|
|
23
|
+
return topic.get_worker_queue(worker_name, queue_max_size)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def get_publisher_queue(queue_name, topic_name)
|
|
29
|
+
if queue_name
|
|
30
|
+
@queue_hash_mutex.synchronize do
|
|
31
|
+
return @queue_hash[queue_name] ||= Queue.new(queue_name)
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
@topic_hash_mutex.synchronize do
|
|
35
|
+
return @topic_hash[topic_name] ||= Topic.new(topic_name)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
Factory.init
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module QueueAdapter
|
|
3
|
+
module InMem
|
|
4
|
+
class Publisher
|
|
5
|
+
|
|
6
|
+
def initialize(queue_adapter, queue_name, topic_name, options, response_options)
|
|
7
|
+
@queue_name, @topic_name, @options, @response_options = queue_name, topic_name, options, response_options
|
|
8
|
+
@queue = Factory.get_publisher_queue(queue_name, topic_name)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def default_marshal_sym
|
|
12
|
+
:none
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Publish the given object and return the reply_queue as the adapter_info.
|
|
16
|
+
def publish(marshaled_object, marshaler, task_id, props)
|
|
17
|
+
# Since we're in-memory, we'll just unmarshal the object so there is less info to carry around
|
|
18
|
+
object = marshaler.unmarshal(marshaled_object)
|
|
19
|
+
reply_queue = nil
|
|
20
|
+
if @response_options
|
|
21
|
+
reply_queue = ReplyQueue.new("#{@queue}:#{object.to_s}")
|
|
22
|
+
end
|
|
23
|
+
@queue.write([object, reply_queue])
|
|
24
|
+
# Return the object to get sent to with_response below.
|
|
25
|
+
return reply_queue
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# See Qwirk::PublishHandle#read_response for the requirements for this method.
|
|
29
|
+
def with_response(reply_queue, &block)
|
|
30
|
+
raise "Could not find reply_queue for #{@queue}" unless reply_queue
|
|
31
|
+
yield reply_queue
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# See Qwirk::Publisher#create_producer_consumer_pair for the requirements for this method
|
|
35
|
+
def create_producer_consumer_pair(task_id, marshaler)
|
|
36
|
+
consumer_queue = Queue.new("#{@queue}:#{task_id}")
|
|
37
|
+
consumer_queue.max_size = @response_options[:queue_max_size] || 100
|
|
38
|
+
producer = MyTaskProducer.new(@queue, consumer_queue, marshaler, @response_options)
|
|
39
|
+
consumer = MyTaskConsumer.new(@queue, consumer_queue)
|
|
40
|
+
return producer, consumer
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def create_fail_producer_consumer_pair(task_id, marshaler)
|
|
44
|
+
consumer_queue = Queue.new("#{@queue}Fail:#{task_id}")
|
|
45
|
+
# TODO: Unlimitied queue or some form of exception on maximum
|
|
46
|
+
consumer_queue.max_size = -1
|
|
47
|
+
producer = MyTaskProducer.new(@queue, consumer_queue, marshaler, {})
|
|
48
|
+
consumer = MyTaskConsumer.new(@queue, consumer_queue)
|
|
49
|
+
return producer, consumer
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
class MyTaskProducer
|
|
55
|
+
def initialize(producer_queue, consumer_queue, marshaler, response_options)
|
|
56
|
+
@producer_queue = producer_queue
|
|
57
|
+
@consumer_queue = consumer_queue
|
|
58
|
+
@marshaler = marshaler
|
|
59
|
+
@response_options = response_options
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def send(marshaled_object)
|
|
63
|
+
object = @marshaler.unmarshal(marshaled_object)
|
|
64
|
+
@producer_queue.write([object, @consumer_queue])
|
|
65
|
+
return object.object_id
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
class MyTaskConsumer
|
|
70
|
+
attr_reader :stopped
|
|
71
|
+
|
|
72
|
+
def initialize(producer_queue, consumer_queue)
|
|
73
|
+
@producer_queue = producer_queue
|
|
74
|
+
@consumer_queue = consumer_queue
|
|
75
|
+
@stopped = false
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def receive
|
|
79
|
+
message_id, response, worker_name = @consumer_queue.read(self)
|
|
80
|
+
return nil unless response
|
|
81
|
+
return [message_id, response]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def acknowledge_message
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def stop
|
|
88
|
+
return if @stopped
|
|
89
|
+
Qwirk.logger.info "Stopping Task worker #{@consumer_queue}"
|
|
90
|
+
# Don't clobber the session before a reply
|
|
91
|
+
@producer_queue.interrupt_read
|
|
92
|
+
@stopped = true
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module QueueAdapter
|
|
3
|
+
module InMem
|
|
4
|
+
|
|
5
|
+
class Queue
|
|
6
|
+
# TODO: Look into reimplementing using a Ruby Queue which is probably better performant
|
|
7
|
+
# Size of the queue before it write-blocks. If 0, messages will be dropped. If -1, then it's unlimited.
|
|
8
|
+
# TODO: Should implement a queue_full_strategy which would be publish_block, drop_oldest, drop_newest
|
|
9
|
+
attr_accessor :name, :max_size
|
|
10
|
+
|
|
11
|
+
def initialize(name)
|
|
12
|
+
@name = name
|
|
13
|
+
@max_size = 0
|
|
14
|
+
@outstanding_hash_mutex = Mutex.new
|
|
15
|
+
@read_condition = ConditionVariable.new
|
|
16
|
+
@write_condition = ConditionVariable.new
|
|
17
|
+
@stop_condition = ConditionVariable.new
|
|
18
|
+
@array = []
|
|
19
|
+
@stopped = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def size
|
|
23
|
+
@array.size
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def stop
|
|
27
|
+
@stopped = true
|
|
28
|
+
@outstanding_hash_mutex.synchronize do
|
|
29
|
+
@write_condition.broadcast
|
|
30
|
+
until @array.empty?
|
|
31
|
+
@stop_condition.wait(@outstanding_hash_mutex)
|
|
32
|
+
end
|
|
33
|
+
@read_condition.broadcast
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def interrupt_read
|
|
38
|
+
@outstanding_hash_mutex.synchronize do
|
|
39
|
+
@read_condition.broadcast
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Block read until a message or we get stopped. stoppable is an object that responds to stopped (a worker or some kind of consumer)
|
|
44
|
+
def read(stoppable)
|
|
45
|
+
@outstanding_hash_mutex.synchronize do
|
|
46
|
+
until @stopped || stoppable.stopped do
|
|
47
|
+
unless @array.empty?
|
|
48
|
+
@write_condition.signal
|
|
49
|
+
return @array.shift
|
|
50
|
+
end
|
|
51
|
+
@read_condition.wait(@outstanding_hash_mutex)
|
|
52
|
+
end
|
|
53
|
+
return if stoppable.stopped
|
|
54
|
+
# We're not persistent, so even though we're stopped we're going to allow our stoppables to keep reading until the queue's empty
|
|
55
|
+
unless @array.empty?
|
|
56
|
+
@stop_condition.signal
|
|
57
|
+
return @array.shift
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
return nil
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def write(obj)
|
|
64
|
+
@outstanding_hash_mutex.synchronize do
|
|
65
|
+
# We just drop the message if no workers have been configured yet
|
|
66
|
+
while !@stopped
|
|
67
|
+
if @max_size == 0
|
|
68
|
+
Qwirk.logger.warn "No worker for queue #{@name}, dropping message #{obj.inspect}"
|
|
69
|
+
return
|
|
70
|
+
end
|
|
71
|
+
if @max_size < 0 || @array.size < @max_size
|
|
72
|
+
@array << obj
|
|
73
|
+
@read_condition.signal
|
|
74
|
+
return
|
|
75
|
+
end
|
|
76
|
+
# TODO: Let's allow various write_full_modes such as :block, :remove_oldest, ? (Currently only blocks)
|
|
77
|
+
@write_condition.wait(@outstanding_hash_mutex)
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def to_s
|
|
83
|
+
"queue:#{@name}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module QueueAdapter
|
|
3
|
+
module InMem
|
|
4
|
+
|
|
5
|
+
class ReplyQueue
|
|
6
|
+
def initialize(name)
|
|
7
|
+
@name = name
|
|
8
|
+
@outstanding_hash_mutex = Mutex.new
|
|
9
|
+
@read_condition = ConditionVariable.new
|
|
10
|
+
@array = []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def timeout_read(timeout)
|
|
14
|
+
@outstanding_hash_mutex.synchronize do
|
|
15
|
+
return @array.shift unless @array.empty?
|
|
16
|
+
timed_read_condition_wait(timeout)
|
|
17
|
+
return @array.shift
|
|
18
|
+
end
|
|
19
|
+
return nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def write(obj)
|
|
23
|
+
@outstanding_hash_mutex.synchronize do
|
|
24
|
+
@array << obj
|
|
25
|
+
@read_condition.signal
|
|
26
|
+
return
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_s
|
|
31
|
+
"reply_queue:#{@name}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#######
|
|
35
|
+
private
|
|
36
|
+
#######
|
|
37
|
+
|
|
38
|
+
if RUBY_PLATFORM == 'jruby' || RUBY_VERSION[0,3] != '1.8'
|
|
39
|
+
def timed_read_condition_wait(timeout)
|
|
40
|
+
# This method not available in MRI 1.8
|
|
41
|
+
@read_condition.wait(@outstanding_hash_mutex, timeout)
|
|
42
|
+
end
|
|
43
|
+
else
|
|
44
|
+
require 'timeout'
|
|
45
|
+
def timed_read_condition_wait(timeout)
|
|
46
|
+
Timeout.timeout(timeout) do
|
|
47
|
+
@read_condition.wait(@outstanding_hash_mutex)
|
|
48
|
+
end
|
|
49
|
+
rescue Timeout::Error => e
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
module Qwirk
|
|
2
|
+
module QueueAdapter
|
|
3
|
+
module InMem
|
|
4
|
+
|
|
5
|
+
class Topic
|
|
6
|
+
def initialize(name)
|
|
7
|
+
@name = name
|
|
8
|
+
@outstanding_hash_mutex = Mutex.new
|
|
9
|
+
@worker_hash = {}
|
|
10
|
+
@stopped = false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_worker_queue(worker_name, queue_max_size)
|
|
14
|
+
@outstanding_hash_mutex.synchronize do
|
|
15
|
+
queue = @worker_hash[worker_name] ||= Queue.new("#{@name}:#{worker_name}")
|
|
16
|
+
queue.max_size = queue_max_size
|
|
17
|
+
return queue
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def stop
|
|
22
|
+
@stopped = true
|
|
23
|
+
@worker_hash.each_value do |queue|
|
|
24
|
+
queue.stop
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def read
|
|
29
|
+
raise "topic should not have been read for #{name}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def write(obj)
|
|
33
|
+
@outstanding_hash_mutex.synchronize do
|
|
34
|
+
@worker_hash.each_value do |queue|
|
|
35
|
+
if !@stopped
|
|
36
|
+
queue.write(obj)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def to_s
|
|
43
|
+
"topic:#{@name}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Handle Messaging and Queuing using JMS
|
|
2
|
+
module Qwirk
|
|
3
|
+
module QueueAdapter
|
|
4
|
+
module InMem
|
|
5
|
+
class Worker
|
|
6
|
+
attr_reader :stopped
|
|
7
|
+
|
|
8
|
+
def initialize(name, marshaler, queue, queue_name, topic_name)
|
|
9
|
+
@name = name
|
|
10
|
+
@marshaler = marshaler
|
|
11
|
+
@queue = queue
|
|
12
|
+
@queue_name = queue_name
|
|
13
|
+
@topic_name = topic_name
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def receive_message
|
|
17
|
+
message, @reply_queue = @queue.read(self)
|
|
18
|
+
return message
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def acknowledge_message(message)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def send_response(original_message, marshaled_object)
|
|
25
|
+
# We unmarshal so our workers get consistent messages regardless of the adapter
|
|
26
|
+
do_send_response(original_message, @marshaler.unmarshal(marshaled_object))
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def send_exception(original_message, e)
|
|
30
|
+
# TODO: I think exceptions should be recreated fully so no need for marshal/unmarshal?
|
|
31
|
+
do_send_response(original_message, Qwirk::RemoteException.new(e))
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def message_to_object(msg)
|
|
35
|
+
# The publisher has already unmarshaled the object to save hassle here.
|
|
36
|
+
return msg
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def handle_failure(message, fail_queue_name)
|
|
40
|
+
# TODO: Mode for persisting to flat file?
|
|
41
|
+
Qwirk.logger.warn("Dropping message that failed: #{message}")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def stop
|
|
45
|
+
return if @stopped
|
|
46
|
+
@stopped = true
|
|
47
|
+
Qwirk.logger.debug { "Stopping #{self}" }
|
|
48
|
+
@queue.interrupt_read
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
## End of required override methods for worker adapter
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def do_send_response(original_message, object)
|
|
55
|
+
puts "Returning #{object} to queue #{@reply_queue}"
|
|
56
|
+
return unless @reply_queue
|
|
57
|
+
@reply_queue.write([original_message.object_id, object, @name])
|
|
58
|
+
return true
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|