qwirk 0.0.1 → 0.1.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 (89) hide show
  1. data/README.md +9 -14
  2. data/lib/qwirk.rb +26 -17
  3. data/lib/qwirk/adapter.rb +3 -45
  4. data/lib/qwirk/adapter/base.rb +2 -0
  5. data/lib/qwirk/adapter/base/expanding_worker_config.rb +133 -0
  6. data/lib/qwirk/adapter/base/worker_config.rb +104 -0
  7. data/lib/qwirk/adapter/in_memory.rb +13 -0
  8. data/lib/qwirk/{queue_adapter/in_mem → adapter/in_memory}/factory.rb +2 -2
  9. data/lib/qwirk/{queue_adapter/in_mem → adapter/in_memory}/publisher.rb +4 -4
  10. data/lib/qwirk/{queue_adapter/in_mem → adapter/in_memory}/queue.rb +3 -2
  11. data/lib/qwirk/{queue_adapter/in_mem → adapter/in_memory}/reply_queue.rb +3 -2
  12. data/lib/qwirk/{queue_adapter/in_mem → adapter/in_memory}/topic.rb +2 -2
  13. data/lib/qwirk/{queue_adapter/in_mem → adapter/in_memory}/worker.rb +6 -8
  14. data/lib/qwirk/adapter/in_memory/worker_config.rb +50 -0
  15. data/lib/qwirk/adapter/inline.rb +9 -0
  16. data/lib/qwirk/adapter/inline/publisher.rb +86 -0
  17. data/lib/qwirk/adapter/inline/worker.rb +55 -0
  18. data/lib/qwirk/adapter/inline/worker_config.rb +30 -0
  19. data/lib/qwirk/adapter_factory.rb +48 -0
  20. data/lib/qwirk/base_worker.rb +18 -28
  21. data/lib/qwirk/batch/file_worker.rb +4 -4
  22. data/lib/qwirk/manager.rb +11 -8
  23. data/lib/qwirk/marshal_strategy/none.rb +1 -1
  24. data/lib/qwirk/publish_handle.rb +22 -11
  25. data/lib/qwirk/publisher.rb +9 -9
  26. data/lib/qwirk/{request_worker.rb → reply_worker.rb} +3 -3
  27. data/lib/qwirk/worker.rb +27 -29
  28. data/test/jms_fail_test.rb +11 -11
  29. data/test/jms_requestor_block_test.rb +12 -12
  30. data/test/jms_requestor_test.rb +8 -8
  31. data/test/jms_test.rb +10 -10
  32. metadata +104 -185
  33. data/examples/README +0 -1
  34. data/examples/activemq.xml +0 -84
  35. data/examples/advanced_requestor/README.md +0 -15
  36. data/examples/advanced_requestor/base_request_worker.rb +0 -18
  37. data/examples/advanced_requestor/char_count_worker.rb +0 -16
  38. data/examples/advanced_requestor/config.ru +0 -24
  39. data/examples/advanced_requestor/exception_raiser_worker.rb +0 -17
  40. data/examples/advanced_requestor/length_worker.rb +0 -14
  41. data/examples/advanced_requestor/print_worker.rb +0 -14
  42. data/examples/advanced_requestor/publisher.rb +0 -49
  43. data/examples/advanced_requestor/qwirk.yml +0 -16
  44. data/examples/advanced_requestor/reverse_worker.rb +0 -14
  45. data/examples/advanced_requestor/triple_worker.rb +0 -14
  46. data/examples/batch/my_batch_worker.rb +0 -30
  47. data/examples/batch/my_line_worker.rb +0 -8
  48. data/examples/qwirk.yml +0 -20
  49. data/examples/requestor/README.md +0 -13
  50. data/examples/requestor/config.ru +0 -13
  51. data/examples/requestor/qwirk_persist.yml +0 -5
  52. data/examples/requestor/requestor.rb +0 -68
  53. data/examples/requestor/reverse_echo_worker.rb +0 -15
  54. data/examples/setup.rb +0 -13
  55. data/examples/shared/README.md +0 -24
  56. data/examples/shared/config.ru +0 -13
  57. data/examples/shared/publisher.rb +0 -49
  58. data/examples/shared/qwirk_persist.yml +0 -5
  59. data/examples/shared/shared_worker.rb +0 -16
  60. data/examples/simple/README +0 -53
  61. data/examples/simple/bar_worker.rb +0 -10
  62. data/examples/simple/baz_worker.rb +0 -10
  63. data/examples/simple/config.ru +0 -14
  64. data/examples/simple/publisher.rb +0 -49
  65. data/examples/simple/qwirk_persist.yml +0 -4
  66. data/examples/simple/tmp/kahadb/db-1.log +0 -0
  67. data/examples/simple/tmp/kahadb/db.data +0 -0
  68. data/examples/simple/tmp/kahadb/db.redo +0 -0
  69. data/examples/task/README +0 -47
  70. data/examples/task/config.ru +0 -14
  71. data/examples/task/foo_worker.rb +0 -10
  72. data/examples/task/messages.out +0 -1000
  73. data/examples/task/publisher.rb +0 -25
  74. data/examples/task/qwirk_persist.yml +0 -5
  75. data/examples/task/task.rb +0 -36
  76. data/lib/qwirk/queue_adapter.rb +0 -3
  77. data/lib/qwirk/queue_adapter/active_mq.rb +0 -13
  78. data/lib/qwirk/queue_adapter/active_mq/publisher.rb +0 -12
  79. data/lib/qwirk/queue_adapter/active_mq/worker_config.rb +0 -16
  80. data/lib/qwirk/queue_adapter/in_mem.rb +0 -7
  81. data/lib/qwirk/queue_adapter/in_mem/worker_config.rb +0 -59
  82. data/lib/qwirk/queue_adapter/jms.rb +0 -50
  83. data/lib/qwirk/queue_adapter/jms/connection.rb +0 -42
  84. data/lib/qwirk/queue_adapter/jms/consumer.rb +0 -37
  85. data/lib/qwirk/queue_adapter/jms/publisher.rb +0 -126
  86. data/lib/qwirk/queue_adapter/jms/worker.rb +0 -89
  87. data/lib/qwirk/queue_adapter/jms/worker_config.rb +0 -38
  88. data/lib/qwirk/version.rb +0 -3
  89. data/lib/qwirk/worker_config.rb +0 -187
@@ -1,6 +1,6 @@
1
1
  module Qwirk
2
- module QueueAdapter
3
- module InMem
2
+ module Adapter
3
+ module InMemory
4
4
  module Factory
5
5
  class << self
6
6
  def init
@@ -1,9 +1,9 @@
1
1
  module Qwirk
2
- module QueueAdapter
3
- module InMem
2
+ module Adapter
3
+ module InMemory
4
4
  class Publisher
5
5
 
6
- def initialize(queue_adapter, queue_name, topic_name, options, response_options)
6
+ def initialize(adapter_factory, queue_name, topic_name, options, response_options)
7
7
  @queue_name, @topic_name, @options, @response_options = queue_name, topic_name, options, response_options
8
8
  @queue = Factory.get_publisher_queue(queue_name, topic_name)
9
9
  end
@@ -42,7 +42,7 @@ module Qwirk
42
42
 
43
43
  def create_fail_producer_consumer_pair(task_id, marshaler)
44
44
  consumer_queue = Queue.new("#{@queue}Fail:#{task_id}")
45
- # TODO: Unlimitied queue or some form of exception on maximum
45
+ # TODO: Unlimited queue or some form of exception on maximum
46
46
  consumer_queue.max_size = -1
47
47
  producer = MyTaskProducer.new(@queue, consumer_queue, marshaler, {})
48
48
  consumer = MyTaskConsumer.new(@queue, consumer_queue)
@@ -1,6 +1,6 @@
1
1
  module Qwirk
2
- module QueueAdapter
3
- module InMem
2
+ module Adapter
3
+ module InMemory
4
4
 
5
5
  class Queue
6
6
  # TODO: Look into reimplementing using a Ruby Queue which is probably better performant
@@ -24,6 +24,7 @@ module Qwirk
24
24
  end
25
25
 
26
26
  def stop
27
+ return if @stopped
27
28
  @stopped = true
28
29
  @outstanding_hash_mutex.synchronize do
29
30
  @write_condition.broadcast
@@ -1,6 +1,6 @@
1
1
  module Qwirk
2
- module QueueAdapter
3
- module InMem
2
+ module Adapter
3
+ module InMemory
4
4
 
5
5
  class ReplyQueue
6
6
  def initialize(name)
@@ -47,6 +47,7 @@ module Qwirk
47
47
  @read_condition.wait(@outstanding_hash_mutex)
48
48
  end
49
49
  rescue Timeout::Error => e
50
+ return nil
50
51
  end
51
52
  end
52
53
 
@@ -1,6 +1,6 @@
1
1
  module Qwirk
2
- module QueueAdapter
3
- module InMem
2
+ module Adapter
3
+ module InMemory
4
4
 
5
5
  class Topic
6
6
  def initialize(name)
@@ -1,16 +1,14 @@
1
1
  # Handle Messaging and Queuing using JMS
2
2
  module Qwirk
3
- module QueueAdapter
4
- module InMem
3
+ module Adapter
4
+ module InMemory
5
5
  class Worker
6
6
  attr_reader :stopped
7
7
 
8
- def initialize(name, marshaler, queue, queue_name, topic_name)
8
+ def initialize(name, marshaler, queue)
9
9
  @name = name
10
10
  @marshaler = marshaler
11
11
  @queue = queue
12
- @queue_name = queue_name
13
- @topic_name = topic_name
14
12
  end
15
13
 
16
14
  def receive_message
@@ -36,7 +34,7 @@ module Qwirk
36
34
  return msg
37
35
  end
38
36
 
39
- def handle_failure(message, fail_queue_name)
37
+ def handle_failure(message, exception, fail_queue_name)
40
38
  # TODO: Mode for persisting to flat file?
41
39
  Qwirk.logger.warn("Dropping message that failed: #{message}")
42
40
  end
@@ -48,11 +46,11 @@ module Qwirk
48
46
  @queue.interrupt_read
49
47
  end
50
48
 
51
- ## End of required override methods for worker adapter
49
+ ## End of required override methods for worker impl
52
50
  private
53
51
 
54
52
  def do_send_response(original_message, object)
55
- puts "Returning #{object} to queue #{@reply_queue}"
53
+ Qwirk.logger.debug { "Returning #{object} to queue #{@reply_queue}" }
56
54
  return unless @reply_queue
57
55
  @reply_queue.write([original_message.object_id, object, @name])
58
56
  return true
@@ -0,0 +1,50 @@
1
+ # Handle Messaging and Queuing using JMS
2
+ module Qwirk
3
+ module Adapter
4
+ module InMemory
5
+
6
+ class WorkerConfig < Qwirk::Adapter::Base::ExpandingWorkerConfig
7
+
8
+ bean_reader :queue_size, :integer, 'Current count of messages in the queue'
9
+ bean_accessor :queue_max_size, :integer, 'Max messages allowed in the queue', :config_item => true
10
+
11
+ def self.default_marshal_sym
12
+ :none
13
+ end
14
+
15
+ def self.initial_default_config
16
+ super.merge(:queue_max_size => 100)
17
+ end
18
+
19
+ def init
20
+ super
21
+ @queue = Factory.get_worker_queue(self.name, self.queue_name, self.topic_name, @queue_max_size)
22
+ end
23
+
24
+ def create_worker
25
+ Worker.new(self.name, self.marshaler, @queue)
26
+ end
27
+
28
+ def stop
29
+ Qwirk.logger.debug { "Stopping #{self}" }
30
+ @queue.stop
31
+ super
32
+ end
33
+
34
+ def queue_size
35
+ return 0 unless @queue
36
+ @queue.size
37
+ end
38
+
39
+ def queue_max_size
40
+ @queue_max_size
41
+ end
42
+
43
+ def queue_max_size=(max_size)
44
+ @queue_max_size = max_size
45
+ @queue.max_size = max_size if @queue
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,9 @@
1
+ require 'qwirk/adapter/inline/publisher'
2
+ require 'qwirk/adapter/inline/worker_config'
3
+ require 'qwirk/adapter/inline/worker'
4
+
5
+ ::Qwirk.register_adapter(
6
+ :inline,
7
+ ::Qwirk::Adapter::Inline::Publisher,
8
+ ::Qwirk::Adapter::Inline::WorkerConfig
9
+ )
@@ -0,0 +1,86 @@
1
+ module Qwirk
2
+ module Adapter
3
+ module Inline
4
+ class Publisher
5
+
6
+ class MyResponseHandle
7
+ def initialize
8
+ @responses = []
9
+ end
10
+
11
+ def add(original_message_id, response_message, worker_name)
12
+ @responses << [original_message_id, response_message, worker_name]
13
+ end
14
+
15
+ # We're inline so we either have a response or not (not is interpreted as a timeout)
16
+ def timeout_read(timeout)
17
+ @responses.pop
18
+ end
19
+ end
20
+
21
+ def initialize(adapter_factory, queue_name, topic_name, options, response_options)
22
+ @adapter_factory, @queue_name, @topic_name, @options, @response_options = adapter_factory, queue_name, topic_name, options, response_options
23
+ end
24
+
25
+ def default_marshal_sym
26
+ :none
27
+ end
28
+
29
+ # Publish the given object and return the reply_queue as the adapter_info.
30
+ def publish(marshaled_object, marshaler, task_id, props)
31
+ response_handle = @response_options ? MyResponseHandle.new : nil
32
+ # Since we're inline, we'll just unmarshal the object so there is less info to carry around
33
+ object = marshaler.unmarshal(marshaled_object)
34
+ if manager = @adapter_factory.manager
35
+ @message_handled = false
36
+ manager.worker_configs.each do |worker_config|
37
+ if worker_config.active
38
+ if @queue_name && @queue_name == worker_config.queue_name
39
+ run_worker(worker_config, object, response_handle)
40
+ @message_handled = true
41
+ break
42
+ elsif @topic_name && @topic_name == worker_config.topic_name
43
+ run_worker(worker_config, object, response_handle)
44
+ @message_handled = true
45
+ end
46
+ end
47
+ end
48
+ if !@message_handled && !@no_message_handled_warning
49
+ Qwirk.logger.warn("Publish message #{object.inspect} being dropped as no Qwirk worker has been configured to handle it")
50
+ @no_message_handled_warning = true
51
+ end
52
+ elsif !@no_manager_warning
53
+ Qwirk.logger.warn("Publish message #{object.inspect} being dropped as no Qwirk manager has been configured for #{@adapter_factory.key}")
54
+ @no_manager_warning = true
55
+ end
56
+ return response_handle
57
+ end
58
+
59
+ # See Qwirk::PublishHandle#read_response for the requirements for this method.
60
+ def with_response(response_handle, &block)
61
+ raise "Could not find response_handle" unless response_handle
62
+ yield response_handle
63
+ end
64
+
65
+ # See Qwirk::Publisher#create_producer_consumer_pair for the requirements for this method
66
+ def create_producer_consumer_pair(task_id, marshaler)
67
+ # TBD
68
+ end
69
+
70
+ def create_fail_producer_consumer_pair(task_id, marshaler)
71
+ # TBD
72
+ end
73
+
74
+ #######
75
+ private
76
+ #######
77
+ def run_worker(worker_config, object, response_handle)
78
+ worker = worker_config.worker_class.new
79
+ worker.init(0, worker_config)
80
+ worker.impl.response_handle = response_handle
81
+ worker.on_message(object)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,55 @@
1
+ # Handle Messaging and Queuing using JMS
2
+ module Qwirk
3
+ module Adapter
4
+ module Inline
5
+ class Worker
6
+
7
+ attr_accessor :response_handle
8
+
9
+ def initialize(name, marshaler)
10
+ @name = name
11
+ @marshaler = marshaler
12
+ end
13
+
14
+ # We never call worker.start so this method is unnecessary
15
+ def receive_message
16
+ end
17
+
18
+ # We never call worker.start so this method is unnecessary
19
+ def acknowledge_message(message)
20
+ end
21
+
22
+ def send_response(original_message, marshaled_object)
23
+ # We unmarshal so our workers get consistent messages regardless of the adapter
24
+ do_send_response(original_message, @marshaler.unmarshal(marshaled_object))
25
+ end
26
+
27
+ def send_exception(original_message, e)
28
+ do_send_response(original_message, Qwirk::RemoteException.new(e))
29
+ end
30
+
31
+ def message_to_object(msg)
32
+ # The publisher has already unmarshaled the object to save hassle here.
33
+ return msg
34
+ end
35
+
36
+ def handle_failure(message, exception, fail_queue_name)
37
+ Qwirk.logger.warn("Dropping message that failed: #{message}")
38
+ end
39
+
40
+ def stop
41
+ end
42
+
43
+ ## End of required override methods for worker impl
44
+ private
45
+
46
+ def do_send_response(original_message, object)
47
+ Qwirk.logger.debug { "Returning #{object} to queue #{@response_handle}" }
48
+ return false unless @response_handle
49
+ @response_handle.add(original_message.object_id, object, @name)
50
+ return true
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ # Handle Messaging and Queuing using JMS
2
+ module Qwirk
3
+ module Adapter
4
+ module Inline
5
+
6
+ class WorkerConfig < Qwirk::Adapter::Base::WorkerConfig
7
+
8
+ bean_attr_accessor :active, :boolean, 'Whether this worker is active or not', :config_item => true
9
+
10
+ def self.default_marshal_sym
11
+ :none
12
+ end
13
+
14
+ # Define the default config values for the attributes all workers will share. These will be sent as options to the constructor
15
+ def self.initial_default_config
16
+ super.merge(:active => false)
17
+ end
18
+
19
+ # Hack - Steal attribute from expanding_worker_config so test config can share development config
20
+ def min_count=(min_count)
21
+ @active = (min_count > 0)
22
+ end
23
+
24
+ def create_worker
25
+ Worker.new(self.name, self.marshaler)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,48 @@
1
+ module Qwirk
2
+
3
+ # Defines the queuing adapter. Currently, only JMS and InMemory.
4
+ class AdapterFactory
5
+ include Rumx::Bean
6
+
7
+ attr_reader :key, :config, :log_times, :adapter_info, :worker_config_class, :manager
8
+
9
+ @@adapter_hash = {}
10
+
11
+ # Register an adapter by passing in a publisher_class, a worker_config_class and optionally a code block.
12
+ # The code block will expect a config object as it's argument and will return connection information
13
+ # or any other client_information required for this adapter.
14
+ def self.register(key, publisher_class, worker_config_class, &block)
15
+ @@adapter_hash[key] = [publisher_class, worker_config_class, block]
16
+ end
17
+
18
+ def initialize(key, config)
19
+ @key = key
20
+ @config = config.dup
21
+ @log_times = config.delete(:log_times)
22
+
23
+ key = config.delete(:adapter)
24
+ raise "No adapter definition" unless key
25
+ key = key.to_sym
26
+ @publisher_class, @worker_config_class, block = @@adapter_hash[key]
27
+ raise "No adapter matching #{key}" unless @publisher_class
28
+ @adapter_info = block.call(config) if block
29
+ end
30
+
31
+ def create_publisher(options={})
32
+ @publisher_parent ||= Rumx::Beans::Folder.new
33
+ publisher = Publisher.new(self, @config.merge(options))
34
+ @publisher_parent.bean_add_child(publisher.to_s, publisher)
35
+ return publisher
36
+ end
37
+
38
+ def create_manager(options={})
39
+ @manager = Manager.new(self, @config.merge(options))
40
+ bean_add_child(:Workers, @manager)
41
+ return @manager
42
+ end
43
+
44
+ def create_publisher_impl(queue_name, topic_name, options, response_options)
45
+ @publisher_class.new(self, queue_name, topic_name, options, response_options)
46
+ end
47
+ end
48
+ end
@@ -1,7 +1,7 @@
1
1
  require 'rumx'
2
2
 
3
3
  module Qwirk
4
- # TODO: Is this necessary anymore or just put in worker.rb? Decide when flat file queue_adapter is implemented.
4
+ # TODO: Is this necessary anymore or just put in worker.rb? Decide when flat file adapter is implemented.
5
5
  module BaseWorker
6
6
 
7
7
  attr_accessor :index, :thread, :config
@@ -12,52 +12,46 @@ module Qwirk
12
12
  name.sub(/::/, '_')
13
13
  end
14
14
 
15
- # Dynamic class create form WorkerConfig and extended through config_accessor, etc calls that will be defined in the worker
16
- def config_class
17
- @config_class ||= Class.new(WorkerConfig)
18
- end
19
-
20
- # Default values for all the config attributes
21
- def default_config
22
- @default_config ||= WorkerConfig.initial_default_config
15
+ def bean_attrs
16
+ @bean_attrs ||= []
23
17
  end
24
18
 
25
19
  #config_accessor :sleep_time, :float, 'Number of seconds to sleep between messages', 5
26
20
  def config_accessor(name, type, description, default_value=nil)
27
- make_bean_attr(:bean_attr_accessor, name, type, description, default_value)
21
+ self.bean_attrs << [:bean_attr_accessor, name, type, description, default_value]
28
22
  end
29
23
 
30
24
  def config_reader(name, type, description, default_value=nil)
31
- make_bean_attr(:bean_attr_reader, name, type, description, default_value)
25
+ self.bean_attrs << [:bean_attr_reader, name, type, description, default_value]
32
26
  end
33
27
 
34
28
  def config_writer(name, type, description, default_value=nil)
35
- make_bean_attr(:bean_attr_writer, name, type, description, default_value)
29
+ self.bean_attrs << [:bean_attr_writer, name, type, description, default_value]
36
30
  end
37
31
 
38
32
  def define_configs(configs)
39
33
  @configs = configs
40
34
  end
41
35
 
42
- def each_config(&block)
36
+ # For each configuration of this worker, yield the name, extended_worker_config_class (the adapters
37
+ # worker_config extended with this class's config attributes), and the default configuration values.
38
+ def each_config(worker_config_class, &block)
43
39
  # Configs are either defined with a define_configs call or default to a single instance with default_config
40
+ default_config = worker_config_class.initial_default_config
41
+ extended_worker_config_class = Class.new(worker_config_class)
42
+ self.bean_attrs.each do |args|
43
+ attr_method, name, type, description, default_value = args
44
+ extended_worker_config_class.send(attr_method, name, type, description, :config_item => true)
45
+ default_config[name.to_sym] = default_value
46
+ end
44
47
  if @configs
45
48
  @configs.each do |name, config|
46
- yield name, default_config.merge(config)
49
+ yield name, extended_worker_config_class, default_config.merge(config)
47
50
  end
48
51
  else
49
- yield default_name, default_config
52
+ yield default_name, extended_worker_config_class, default_config
50
53
  end
51
54
  end
52
-
53
- #######
54
- private
55
- #######
56
-
57
- def make_bean_attr(attr_method, name, type, description, default_value)
58
- config_class.send(attr_method, name, type, description, :config_item => true)
59
- default_config[name.to_sym] = default_value
60
- end
61
55
  end
62
56
 
63
57
  def self.included(base)
@@ -73,10 +67,6 @@ module Qwirk
73
67
  @worker_classes ||= []
74
68
  end
75
69
 
76
- def start
77
- raise "Need to override start method in #{self.class.name}"
78
- end
79
-
80
70
  def stop
81
71
  raise "Need to override stop method in #{self.class.name}"
82
72
  end