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.
Files changed (154) hide show
  1. data/History.md +7 -0
  2. data/LICENSE.txt +201 -0
  3. data/README.md +180 -0
  4. data/Rakefile +34 -0
  5. data/examples/README +1 -0
  6. data/examples/activemq.xml +84 -0
  7. data/examples/advanced_requestor/README.md +15 -0
  8. data/examples/advanced_requestor/base_request_worker.rb +18 -0
  9. data/examples/advanced_requestor/char_count_worker.rb +16 -0
  10. data/examples/advanced_requestor/config.ru +24 -0
  11. data/examples/advanced_requestor/exception_raiser_worker.rb +17 -0
  12. data/examples/advanced_requestor/length_worker.rb +14 -0
  13. data/examples/advanced_requestor/print_worker.rb +14 -0
  14. data/examples/advanced_requestor/publisher.rb +49 -0
  15. data/examples/advanced_requestor/qwirk.yml +16 -0
  16. data/examples/advanced_requestor/reverse_worker.rb +14 -0
  17. data/examples/advanced_requestor/triple_worker.rb +14 -0
  18. data/examples/batch/my_batch_worker.rb +30 -0
  19. data/examples/batch/my_line_worker.rb +8 -0
  20. data/examples/qwirk.yml +20 -0
  21. data/examples/requestor/README.md +13 -0
  22. data/examples/requestor/config.ru +13 -0
  23. data/examples/requestor/qwirk_persist.yml +5 -0
  24. data/examples/requestor/requestor.rb +68 -0
  25. data/examples/requestor/reverse_echo_worker.rb +15 -0
  26. data/examples/setup.rb +13 -0
  27. data/examples/shared/README.md +24 -0
  28. data/examples/shared/config.ru +13 -0
  29. data/examples/shared/publisher.rb +49 -0
  30. data/examples/shared/qwirk_persist.yml +5 -0
  31. data/examples/shared/shared_worker.rb +16 -0
  32. data/examples/simple/README +53 -0
  33. data/examples/simple/bar_worker.rb +10 -0
  34. data/examples/simple/baz_worker.rb +10 -0
  35. data/examples/simple/config.ru +14 -0
  36. data/examples/simple/publisher.rb +49 -0
  37. data/examples/simple/qwirk_persist.yml +4 -0
  38. data/examples/simple/tmp/kahadb/db-1.log +0 -0
  39. data/examples/simple/tmp/kahadb/db.data +0 -0
  40. data/examples/simple/tmp/kahadb/db.redo +0 -0
  41. data/examples/task/README +47 -0
  42. data/examples/task/config.ru +14 -0
  43. data/examples/task/foo_worker.rb +10 -0
  44. data/examples/task/messages.out +1000 -0
  45. data/examples/task/publisher.rb +25 -0
  46. data/examples/task/qwirk_persist.yml +5 -0
  47. data/examples/task/task.rb +36 -0
  48. data/lib/qwirk.rb +63 -0
  49. data/lib/qwirk/adapter.rb +45 -0
  50. data/lib/qwirk/base_worker.rb +96 -0
  51. data/lib/qwirk/batch.rb +4 -0
  52. data/lib/qwirk/batch/acquire_file_strategy.rb +47 -0
  53. data/lib/qwirk/batch/active_record.rb +3 -0
  54. data/lib/qwirk/batch/active_record/batch_job.rb +111 -0
  55. data/lib/qwirk/batch/active_record/failed_record.rb +5 -0
  56. data/lib/qwirk/batch/active_record/outstanding_record.rb +6 -0
  57. data/lib/qwirk/batch/file_status_strategy.rb +86 -0
  58. data/lib/qwirk/batch/file_worker.rb +228 -0
  59. data/lib/qwirk/batch/job_status.rb +29 -0
  60. data/lib/qwirk/batch/parse_file_strategy.rb +48 -0
  61. data/lib/qwirk/engine.rb +9 -0
  62. data/lib/qwirk/loggable.rb +23 -0
  63. data/lib/qwirk/manager.rb +140 -0
  64. data/lib/qwirk/marshal_strategy.rb +74 -0
  65. data/lib/qwirk/marshal_strategy/bson.rb +37 -0
  66. data/lib/qwirk/marshal_strategy/json.rb +37 -0
  67. data/lib/qwirk/marshal_strategy/none.rb +26 -0
  68. data/lib/qwirk/marshal_strategy/ruby.rb +26 -0
  69. data/lib/qwirk/marshal_strategy/string.rb +25 -0
  70. data/lib/qwirk/marshal_strategy/yaml.rb +25 -0
  71. data/lib/qwirk/publish_handle.rb +170 -0
  72. data/lib/qwirk/publisher.rb +67 -0
  73. data/lib/qwirk/queue_adapter.rb +3 -0
  74. data/lib/qwirk/queue_adapter/active_mq.rb +13 -0
  75. data/lib/qwirk/queue_adapter/active_mq/publisher.rb +12 -0
  76. data/lib/qwirk/queue_adapter/active_mq/worker_config.rb +16 -0
  77. data/lib/qwirk/queue_adapter/in_mem.rb +7 -0
  78. data/lib/qwirk/queue_adapter/in_mem/factory.rb +45 -0
  79. data/lib/qwirk/queue_adapter/in_mem/publisher.rb +98 -0
  80. data/lib/qwirk/queue_adapter/in_mem/queue.rb +88 -0
  81. data/lib/qwirk/queue_adapter/in_mem/reply_queue.rb +56 -0
  82. data/lib/qwirk/queue_adapter/in_mem/topic.rb +48 -0
  83. data/lib/qwirk/queue_adapter/in_mem/worker.rb +63 -0
  84. data/lib/qwirk/queue_adapter/in_mem/worker_config.rb +59 -0
  85. data/lib/qwirk/queue_adapter/jms.rb +50 -0
  86. data/lib/qwirk/queue_adapter/jms/connection.rb +42 -0
  87. data/lib/qwirk/queue_adapter/jms/consumer.rb +37 -0
  88. data/lib/qwirk/queue_adapter/jms/publisher.rb +126 -0
  89. data/lib/qwirk/queue_adapter/jms/worker.rb +89 -0
  90. data/lib/qwirk/queue_adapter/jms/worker_config.rb +38 -0
  91. data/lib/qwirk/remote_exception.rb +42 -0
  92. data/lib/qwirk/request_worker.rb +62 -0
  93. data/lib/qwirk/task.rb +177 -0
  94. data/lib/qwirk/task.rb.sav +194 -0
  95. data/lib/qwirk/version.rb +3 -0
  96. data/lib/qwirk/worker.rb +222 -0
  97. data/lib/qwirk/worker_config.rb +187 -0
  98. data/lib/rails/generators/qwirk/qwirk_generator.rb +82 -0
  99. data/lib/rails/generators/qwirk/templates/initializer.rb +6 -0
  100. data/lib/rails/generators/qwirk/templates/migration.rb +9 -0
  101. data/lib/rails/generators/qwirk/templates/schema.rb +28 -0
  102. data/lib/rails/railties/tasks.rake +8 -0
  103. data/lib/tasks/qwirk_tasks.rake +4 -0
  104. data/test/base_test.rb +248 -0
  105. data/test/database.yml +14 -0
  106. data/test/dummy/Rakefile +7 -0
  107. data/test/dummy/app/controllers/application_controller.rb +3 -0
  108. data/test/dummy/app/helpers/application_helper.rb +2 -0
  109. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  110. data/test/dummy/config.ru +4 -0
  111. data/test/dummy/config/application.rb +45 -0
  112. data/test/dummy/config/boot.rb +10 -0
  113. data/test/dummy/config/database.yml +22 -0
  114. data/test/dummy/config/environment.rb +5 -0
  115. data/test/dummy/config/environments/development.rb +26 -0
  116. data/test/dummy/config/environments/production.rb +49 -0
  117. data/test/dummy/config/environments/test.rb +35 -0
  118. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  119. data/test/dummy/config/initializers/inflections.rb +10 -0
  120. data/test/dummy/config/initializers/mime_types.rb +5 -0
  121. data/test/dummy/config/initializers/secret_token.rb +7 -0
  122. data/test/dummy/config/initializers/session_store.rb +8 -0
  123. data/test/dummy/config/locales/en.yml +5 -0
  124. data/test/dummy/config/routes.rb +58 -0
  125. data/test/dummy/log/development.log +0 -0
  126. data/test/dummy/log/production.log +0 -0
  127. data/test/dummy/log/server.log +0 -0
  128. data/test/dummy/log/test.log +0 -0
  129. data/test/dummy/public/404.html +26 -0
  130. data/test/dummy/public/422.html +26 -0
  131. data/test/dummy/public/500.html +26 -0
  132. data/test/dummy/public/favicon.ico +0 -0
  133. data/test/dummy/public/javascripts/application.js +2 -0
  134. data/test/dummy/public/javascripts/controls.js +965 -0
  135. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  136. data/test/dummy/public/javascripts/effects.js +1123 -0
  137. data/test/dummy/public/javascripts/prototype.js +6001 -0
  138. data/test/dummy/public/javascripts/rails.js +191 -0
  139. data/test/dummy/script/rails +6 -0
  140. data/test/integration/navigation_test.rb +7 -0
  141. data/test/jms.yml +9 -0
  142. data/test/jms_fail_test.rb +149 -0
  143. data/test/jms_requestor_block_test.rb +278 -0
  144. data/test/jms_requestor_test.rb +238 -0
  145. data/test/jms_test.rb +287 -0
  146. data/test/marshal_strategy_test.rb +62 -0
  147. data/test/support/integration_case.rb +5 -0
  148. data/test/test_helper.rb +7 -0
  149. data/test/test_helper.rbold +22 -0
  150. data/test/test_helper_active_record.rb +61 -0
  151. data/test/unit/qwirk/batch/acquire_file_strategy_test.rb +101 -0
  152. data/test/unit/qwirk/batch/active_record/batch_job_test.rb +35 -0
  153. data/test/unit/qwirk/batch/parse_file_strategy_test.rb +49 -0
  154. metadata +366 -0
@@ -0,0 +1,191 @@
1
+ (function() {
2
+ // Technique from Juriy Zaytsev
3
+ // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
4
+ function isEventSupported(eventName) {
5
+ var el = document.createElement('div');
6
+ eventName = 'on' + eventName;
7
+ var isSupported = (eventName in el);
8
+ if (!isSupported) {
9
+ el.setAttribute(eventName, 'return;');
10
+ isSupported = typeof el[eventName] == 'function';
11
+ }
12
+ el = null;
13
+ return isSupported;
14
+ }
15
+
16
+ function isForm(element) {
17
+ return Object.isElement(element) && element.nodeName.toUpperCase() == 'FORM'
18
+ }
19
+
20
+ function isInput(element) {
21
+ if (Object.isElement(element)) {
22
+ var name = element.nodeName.toUpperCase()
23
+ return name == 'INPUT' || name == 'SELECT' || name == 'TEXTAREA'
24
+ }
25
+ else return false
26
+ }
27
+
28
+ var submitBubbles = isEventSupported('submit'),
29
+ changeBubbles = isEventSupported('change')
30
+
31
+ if (!submitBubbles || !changeBubbles) {
32
+ // augment the Event.Handler class to observe custom events when needed
33
+ Event.Handler.prototype.initialize = Event.Handler.prototype.initialize.wrap(
34
+ function(init, element, eventName, selector, callback) {
35
+ init(element, eventName, selector, callback)
36
+ // is the handler being attached to an element that doesn't support this event?
37
+ if ( (!submitBubbles && this.eventName == 'submit' && !isForm(this.element)) ||
38
+ (!changeBubbles && this.eventName == 'change' && !isInput(this.element)) ) {
39
+ // "submit" => "emulated:submit"
40
+ this.eventName = 'emulated:' + this.eventName
41
+ }
42
+ }
43
+ )
44
+ }
45
+
46
+ if (!submitBubbles) {
47
+ // discover forms on the page by observing focus events which always bubble
48
+ document.on('focusin', 'form', function(focusEvent, form) {
49
+ // special handler for the real "submit" event (one-time operation)
50
+ if (!form.retrieve('emulated:submit')) {
51
+ form.on('submit', function(submitEvent) {
52
+ var emulated = form.fire('emulated:submit', submitEvent, true)
53
+ // if custom event received preventDefault, cancel the real one too
54
+ if (emulated.returnValue === false) submitEvent.preventDefault()
55
+ })
56
+ form.store('emulated:submit', true)
57
+ }
58
+ })
59
+ }
60
+
61
+ if (!changeBubbles) {
62
+ // discover form inputs on the page
63
+ document.on('focusin', 'input, select, texarea', function(focusEvent, input) {
64
+ // special handler for real "change" events
65
+ if (!input.retrieve('emulated:change')) {
66
+ input.on('change', function(changeEvent) {
67
+ input.fire('emulated:change', changeEvent, true)
68
+ })
69
+ input.store('emulated:change', true)
70
+ }
71
+ })
72
+ }
73
+
74
+ function handleRemote(element) {
75
+ var method, url, params;
76
+
77
+ var event = element.fire("ajax:before");
78
+ if (event.stopped) return false;
79
+
80
+ if (element.tagName.toLowerCase() === 'form') {
81
+ method = element.readAttribute('method') || 'post';
82
+ url = element.readAttribute('action');
83
+ params = element.serialize();
84
+ } else {
85
+ method = element.readAttribute('data-method') || 'get';
86
+ url = element.readAttribute('href');
87
+ params = {};
88
+ }
89
+
90
+ new Ajax.Request(url, {
91
+ method: method,
92
+ parameters: params,
93
+ evalScripts: true,
94
+
95
+ onComplete: function(request) { element.fire("ajax:complete", request); },
96
+ onSuccess: function(request) { element.fire("ajax:success", request); },
97
+ onFailure: function(request) { element.fire("ajax:failure", request); }
98
+ });
99
+
100
+ element.fire("ajax:after");
101
+ }
102
+
103
+ function handleMethod(element) {
104
+ var method = element.readAttribute('data-method'),
105
+ url = element.readAttribute('href'),
106
+ csrf_param = $$('meta[name=csrf-param]')[0],
107
+ csrf_token = $$('meta[name=csrf-token]')[0];
108
+
109
+ var form = new Element('form', { method: "POST", action: url, style: "display: none;" });
110
+ element.parentNode.insert(form);
111
+
112
+ if (method !== 'post') {
113
+ var field = new Element('input', { type: 'hidden', name: '_method', value: method });
114
+ form.insert(field);
115
+ }
116
+
117
+ if (csrf_param) {
118
+ var param = csrf_param.readAttribute('content'),
119
+ token = csrf_token.readAttribute('content'),
120
+ field = new Element('input', { type: 'hidden', name: param, value: token });
121
+ form.insert(field);
122
+ }
123
+
124
+ form.submit();
125
+ }
126
+
127
+
128
+ document.on("click", "*[data-confirm]", function(event, element) {
129
+ var message = element.readAttribute('data-confirm');
130
+ if (!confirm(message)) event.stop();
131
+ });
132
+
133
+ document.on("click", "a[data-remote]", function(event, element) {
134
+ if (event.stopped) return;
135
+ handleRemote(element);
136
+ event.stop();
137
+ });
138
+
139
+ document.on("click", "a[data-method]", function(event, element) {
140
+ if (event.stopped) return;
141
+ handleMethod(element);
142
+ event.stop();
143
+ });
144
+
145
+ document.on("submit", function(event) {
146
+ var element = event.findElement(),
147
+ message = element.readAttribute('data-confirm');
148
+ if (message && !confirm(message)) {
149
+ event.stop();
150
+ return false;
151
+ }
152
+
153
+ var inputs = element.select("input[type=submit][data-disable-with]");
154
+ inputs.each(function(input) {
155
+ input.disabled = true;
156
+ input.writeAttribute('data-original-value', input.value);
157
+ input.value = input.readAttribute('data-disable-with');
158
+ });
159
+
160
+ var element = event.findElement("form[data-remote]");
161
+ if (element) {
162
+ handleRemote(element);
163
+ event.stop();
164
+ }
165
+ });
166
+
167
+ document.on("ajax:after", "form", function(event, element) {
168
+ var inputs = element.select("input[type=submit][disabled=true][data-disable-with]");
169
+ inputs.each(function(input) {
170
+ input.value = input.readAttribute('data-original-value');
171
+ input.removeAttribute('data-original-value');
172
+ input.disabled = false;
173
+ });
174
+ });
175
+
176
+ Ajax.Responders.register({
177
+ onCreate: function(request) {
178
+ var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
179
+
180
+ if (csrf_meta_tag) {
181
+ var header = 'X-CSRF-Token',
182
+ token = csrf_meta_tag.readAttribute('content');
183
+
184
+ if (!request.options.requestHeaders) {
185
+ request.options.requestHeaders = {};
186
+ }
187
+ request.options.requestHeaders[header] = token;
188
+ }
189
+ }
190
+ });
191
+ })();
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env jruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class NavigationTest < ActiveSupport::IntegrationCase
4
+ test "truth" do
5
+ assert_kind_of Dummy::Application, Rails.application
6
+ end
7
+ end
data/test/jms.yml ADDED
@@ -0,0 +1,9 @@
1
+ :factory: org.apache.activemq.ActiveMQConnectionFactory
2
+ :broker_url: tcp://localhost:61616
3
+ :log_times: false
4
+ :require_jars:
5
+ - <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/activemq-all-*.jar")[0] %>
6
+ - <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/lib/optional/slf4j-log4j*.jar")[0] %>
7
+ - <%= Dir.glob("#{ENV['ACTIVEMQ_HOME']}/lib/optional/log4j-*.jar")[0] %>
8
+ #:username: myuser
9
+ #:password: mypassword
@@ -0,0 +1,149 @@
1
+ require 'qwirk'
2
+ require 'shoulda'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+ require 'erb'
6
+
7
+ # NOTE: This test requires a running ActiveMQ server
8
+
9
+ class ExceptionWorker
10
+ include Qwirk::QueueAdapter::JMS::Worker
11
+ def perform(obj)
12
+ puts "#{name} received #{obj} but raising exception"
13
+ raise 'foobar'
14
+ end
15
+
16
+ def log_backtrace(e)
17
+ end
18
+ end
19
+
20
+ class ExceptionRequestWorker
21
+ include Qwirk::QueueAdapter::JMS::RequestWorker
22
+
23
+ def request(obj)
24
+ puts "#{name} received #{obj} but raising exception"
25
+ raise 'foobar'
26
+ end
27
+
28
+ def log_backtrace(e)
29
+ end
30
+ end
31
+
32
+ # This will read from the queue that ExceptionWorker fails to
33
+ class ExceptionFailWorker
34
+ include Qwirk::QueueAdapter::JMS::Worker
35
+
36
+ @@my_hash = {}
37
+
38
+ def self.my_obj(name)
39
+ @@my_hash[name]
40
+ end
41
+
42
+ def perform(obj)
43
+ puts "#{name} received #{obj}"
44
+ @@my_hash[name] = obj
45
+ end
46
+ end
47
+
48
+ class JMSFailTest < Test::Unit::TestCase
49
+
50
+ def assert_fail_queue(queue_name, fail_queue_name, value, is_fail_queue_expected)
51
+ # Publish to Exception that will throw exception which will put on ExceptionFail queue
52
+ publisher = Qwirk::QueueAdapter::JMS::Publisher.new(:queue_name => queue_name, :marshal => :string)
53
+ puts "Publishing #{value} to #{queue_name}"
54
+ publisher.publish(value)
55
+ sleep 1
56
+ expected_value = (is_fail_queue_expected ? value : nil)
57
+ assert_equal expected_value, ExceptionFailWorker.my_obj(fail_queue_name)
58
+ end
59
+
60
+ context 'jms' do
61
+ setup do
62
+ config = YAML.load(ERB.new(File.read(File.join(File.dirname(__FILE__), 'jms.yml'))).result(binding))
63
+ Qwirk::QueueAdapter::JMS::Connection.init(config)
64
+ end
65
+
66
+ teardown do
67
+ end
68
+
69
+ context "worker with exception" do
70
+ setup do
71
+ @manager = Qwirk::Manager.new
72
+
73
+ # Should receive message on the fail worker when using with default names
74
+ @manager.add(ExceptionWorker, 1)
75
+ @manager.add(ExceptionFailWorker, 1)
76
+
77
+ # Should receive message on the fail worker when using specified names
78
+ name = 'ExceptionNameSpecified'
79
+ @manager.add(ExceptionWorker, 1, :name => name)
80
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
81
+
82
+ # Should receive message on the fail worker when using specified names and fail_queue set true
83
+ name = 'ExceptionFailQueueTrue'
84
+ @manager.add(ExceptionWorker, 1, :name => name, :fail_queue => true)
85
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
86
+
87
+ # Should NOT receive message on the fail worker when using specified names and fail_queue set false
88
+ name = 'ExceptionFailQueueFalse'
89
+ @manager.add(ExceptionWorker, 1, :name => name, :fail_queue => false)
90
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
91
+
92
+ # Should NOT receive message on the fail worker when using specified names and fail_queue set false
93
+ name = 'ExceptionFailQueueSpecified'
94
+ fail_queue = 'MyFailQueue'
95
+ @manager.add(ExceptionWorker, 1, :name => name, :fail_queue => fail_queue)
96
+ @manager.add(ExceptionFailWorker, 1, :name => fail_queue)
97
+
98
+ # Should NOT receive message on the fail worker
99
+ name = 'ExceptionRequest'
100
+ @manager.add(ExceptionRequestWorker, 1)
101
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
102
+
103
+ # Should NOT receive message on the fail worker when using specified names
104
+ name = 'ExceptionRequestNameSpecified'
105
+ @manager.add(ExceptionRequestWorker, 1, :name => name)
106
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
107
+
108
+ # Should receive message on the fail worker when using specified names and fail_queue set true
109
+ name = 'ExceptionRequestFailQueueTrue'
110
+ @manager.add(ExceptionRequestWorker, 1, :name => name, :fail_queue => true)
111
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
112
+
113
+ # Should NOT receive message on the fail worker when using specified names and fail_queue set false
114
+ name = 'ExceptionRequestFailQueueFalse'
115
+ @manager.add(ExceptionRequestWorker, 1, :name => name, :fail_queue => false)
116
+ @manager.add(ExceptionFailWorker, 1, :name => "#{name}Fail")
117
+
118
+ # Should NOT receive message on the fail worker when using specified names and fail_queue set false
119
+ name = 'ExceptionRequestFailQueueSpecified'
120
+ fail_queue = 'MyRequestFailQueue'
121
+ @manager.add(ExceptionRequestWorker, 1, :name => name, :fail_queue => fail_queue)
122
+ @manager.add(ExceptionFailWorker, 1, :name => fail_queue)
123
+
124
+ sleep 1
125
+ end
126
+
127
+ teardown do
128
+ if @manager
129
+ @manager.stop
130
+ @manager.join
131
+ end
132
+ end
133
+
134
+ should "write fail messages to a fail queue" do
135
+ assert_fail_queue('Exception', 'ExceptionFail', 'value0', true)
136
+ assert_fail_queue('ExceptionNameSpecified', 'ExceptionNameSpecifiedFail', 'value1', true)
137
+ assert_fail_queue('ExceptionFailQueueTrue', 'ExceptionFailQueueTrueFail', 'value2', true)
138
+ assert_fail_queue('ExceptionFailQueueFalse', 'ExceptionFailQueueFalseFail', 'value3', false)
139
+ assert_fail_queue('ExceptionFailQueueSpecified', 'MyFailQueue', 'value4', true)
140
+
141
+ assert_fail_queue('ExceptionRequest', 'ExceptionRequestFail', 'value5', false)
142
+ assert_fail_queue('ExceptionRequestNameSpecified', 'ExceptionRequestNameSpecifiedFail', 'value6', false)
143
+ assert_fail_queue('ExceptionRequestFailQueueTrue', 'ExceptionRequestFailQueueTrueFail', 'value7', true)
144
+ assert_fail_queue('ExceptionRequestFailQueueFalse', 'ExceptionRequestFailQueueFalseFail', 'value8', false)
145
+ assert_fail_queue('ExceptionRequestFailQueueSpecified', 'MyRequestFailQueue', 'value9', true)
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,278 @@
1
+ require 'qwirk'
2
+ require 'shoulda'
3
+ require 'test/unit'
4
+ require 'fileutils'
5
+ require 'erb'
6
+
7
+ # NOTE: This test requires a running ActiveMQ server
8
+
9
+ class BaseRequestWorker
10
+ include Qwirk::QueueAdapter::JMS::RequestWorker
11
+
12
+ def self.sleep_time=(val)
13
+ @sleep_time = val
14
+ end
15
+
16
+ def self.sleep_time
17
+ @sleep_time
18
+ end
19
+
20
+ def self.do_exception=(val)
21
+ @create_exception = val
22
+ end
23
+
24
+ def self.do_exception
25
+ @create_exception
26
+ end
27
+
28
+ def request(obj)
29
+ raise Exception,'my exception' if self.class.do_exception
30
+ sleep self.class.sleep_time if self.class.sleep_time
31
+ end
32
+
33
+ def log_backtrace(e)
34
+ end
35
+ end
36
+
37
+ class CharCountWorker < BaseRequestWorker
38
+ virtual_topic 'test_string'
39
+ response :marshal => :bson
40
+
41
+ def request(obj)
42
+ super
43
+ hash = Hash.new(0)
44
+ obj.each_char {|c| hash[c] += 1}
45
+ hash
46
+ end
47
+ end
48
+
49
+ class LengthWorker < BaseRequestWorker
50
+ virtual_topic 'test_string'
51
+ response :marshal => :ruby
52
+
53
+ def request(obj)
54
+ super
55
+ obj.length
56
+ end
57
+ end
58
+
59
+ class ReverseWorker < BaseRequestWorker
60
+ virtual_topic 'test_string'
61
+ response :marshal => :string
62
+
63
+ def request(obj)
64
+ super
65
+ obj.reverse
66
+ end
67
+ end
68
+
69
+ class TripleWorker < BaseRequestWorker
70
+ virtual_topic 'test_string'
71
+ response :marshal => :string
72
+
73
+ def request(obj)
74
+ super
75
+ obj*3
76
+ end
77
+ end
78
+
79
+ class HolderWorker
80
+ include Qwirk::QueueAdapter::JMS::Worker
81
+ virtual_topic 'test_string'
82
+
83
+ def self.my_obj
84
+ @@my_obj
85
+ end
86
+
87
+ def perform(obj)
88
+ @@my_obj = obj
89
+ end
90
+ end
91
+
92
+ class JMSRequestorBlockTest < Test::Unit::TestCase
93
+
94
+ def assert_response(hash, expected_key, expected_val)
95
+ assert_equal 1, hash.keys.size
96
+ actual_val = hash[expected_key]
97
+ assert_equal expected_val, actual_val
98
+ end
99
+
100
+ def assert_exception(hash, expected_key)
101
+ assert_equal 1, hash.keys.size
102
+ e = hash[expected_key]
103
+
104
+ assert e.kind_of?(Qwirk::RemoteException)
105
+ end
106
+
107
+ def make_call(publisher, string, timeout)
108
+ handle = publisher.publish(string)
109
+ hash_results = {}
110
+
111
+ handle.read_response(timeout) do |response|
112
+ response.on_message 'CharCount' do |val|
113
+ hash = hash_results[response.name] ||= {}
114
+ hash[:message] = val
115
+ end
116
+ response.on_message 'CharCount2' do |val|
117
+ hash = hash_results[response.name] ||= {}
118
+ hash[:message] = val
119
+ end
120
+ response.on_message 'Length', 'Length2', 'Reverse', 'Triple' do |val|
121
+ hash = hash_results[response.name] ||= {}
122
+ hash[:message] = val
123
+ end
124
+ response.on_timeout 'Reverse' do
125
+ hash = hash_results[response.name] ||= {}
126
+ hash[:explicit_timeout] = true
127
+ end
128
+ response.on_timeout do
129
+ hash = hash_results[response.name] ||= {}
130
+ hash[:default_timeout] = true
131
+ end
132
+ response.on_remote_exception 'CharCount' do |e|
133
+ hash = hash_results[response.name] ||= {}
134
+ hash[:explicit_exception] = e
135
+ end
136
+ response.on_remote_exception do |e|
137
+ hash = hash_results[response.name] ||= {}
138
+ hash[:default_exception] = e
139
+ end
140
+ end
141
+ puts "results=#{hash_results.inspect}"
142
+ # 6 request workers
143
+ assert_equal 6, hash_results.keys.size
144
+ assert_equal string, HolderWorker.my_obj
145
+ return hash_results
146
+ end
147
+
148
+ context 'jms request with block' do
149
+ setup do
150
+ config = YAML.load(ERB.new(File.read(File.join(File.dirname(__FILE__), 'jms.yml'))).result(binding))
151
+ Qwirk::QueueAdapter::JMS::Connection.init(config)
152
+ end
153
+
154
+ teardown do
155
+ end
156
+
157
+ context "real publishing" do
158
+ setup do
159
+ @manager = Qwirk::Manager.new
160
+
161
+ @manager.add(CharCountWorker, 1)
162
+ @manager.add(CharCountWorker, 1, :name => 'CharCount2')
163
+ @manager.add(LengthWorker, 1)
164
+ @manager.add(LengthWorker, 1, :name => 'Length2')
165
+ @manager.add(ReverseWorker, 1)
166
+ @manager.add(TripleWorker, 1)
167
+ @manager.add(HolderWorker, 1)
168
+
169
+ sleep 1
170
+ end
171
+
172
+ teardown do
173
+ if @manager
174
+ @manager.stop
175
+ @manager.join
176
+ end
177
+ end
178
+
179
+ should "handle replies" do
180
+
181
+ publisher = Qwirk::QueueAdapter::JMS::Publisher.new(:virtual_topic_name => 'test_string', :response_time_to_live => 10000, :marshal => :string)
182
+ cc_val = {'f' => 1, 'o' => 4, 'b' => 1}
183
+
184
+ hash = make_call(publisher, 'fooboo', 2)
185
+ assert_response(hash['CharCount'], :message, cc_val)
186
+ assert_response(hash['CharCount2'], :message, cc_val)
187
+ assert_response(hash['Length'], :message, 6)
188
+ assert_response(hash['Length2'], :message, 6)
189
+ assert_response(hash['Reverse'], :message, 'ooboof')
190
+ assert_response(hash['Triple'], :message, 'fooboofooboofooboo')
191
+
192
+ CharCountWorker.sleep_time = 3
193
+ ReverseWorker.sleep_time = 3
194
+ hash = make_call(publisher, 'fooboo', 2)
195
+ assert_response(hash['CharCount'], :default_timeout, true)
196
+ assert_response(hash['CharCount2'], :default_timeout, true)
197
+ assert_response(hash['Length'], :message, 6)
198
+ assert_response(hash['Length2'], :message, 6)
199
+ assert_response(hash['Reverse'], :explicit_timeout, true)
200
+ assert_response(hash['Triple'], :message, 'fooboofooboofooboo')
201
+ CharCountWorker.sleep_time = nil
202
+ ReverseWorker.sleep_time = nil
203
+
204
+ CharCountWorker.do_exception = true
205
+ TripleWorker.do_exception = true
206
+ hash = make_call(publisher, 'fooboo', 2)
207
+ assert_exception(hash['CharCount'], :explicit_exception)
208
+ assert_exception(hash['CharCount2'], :default_exception)
209
+ assert_response(hash['Length'], :message, 6)
210
+ assert_response(hash['Length2'], :message, 6)
211
+ assert_response(hash['Reverse'], :message, 'ooboof')
212
+ assert_exception(hash['Triple'], :default_exception)
213
+ CharCountWorker.do_exception = false
214
+ TripleWorker.do_exception = false
215
+
216
+ sleep 2
217
+ end
218
+ end
219
+
220
+ context "dummy publishing" do
221
+ setup do
222
+ workers = [
223
+ CharCountWorker.new,
224
+ CharCountWorker.new(:name => 'CharCount2'),
225
+ LengthWorker.new,
226
+ LengthWorker.new(:name => 'Length2'),
227
+ ReverseWorker.new,
228
+ TripleWorker.new,
229
+ HolderWorker.new,
230
+ ]
231
+ Qwirk::QueueAdapter::JMS::Publisher.setup_dummy_publishing(workers)
232
+ end
233
+
234
+ teardown do
235
+ Qwirk::QueueAdapter::JMS::Publisher.clear_dummy_publishing
236
+ end
237
+
238
+ should "handle replies" do
239
+
240
+ publisher = Qwirk::QueueAdapter::JMS::Publisher.new(:virtual_topic_name => 'test_string', :response => true, :marshal => :string)
241
+ cc_val = {'f' => 1, 'o' => 4, 'b' => 1}
242
+
243
+ hash = make_call(publisher, 'fooboo', 2)
244
+ assert_response(hash['CharCount'], :message, cc_val)
245
+ assert_response(hash['CharCount2'], :message, cc_val)
246
+ assert_response(hash['Length'], :message, 6)
247
+ assert_response(hash['Length2'], :message, 6)
248
+ assert_response(hash['Reverse'], :message, 'ooboof')
249
+ assert_response(hash['Triple'], :message, 'fooboofooboofooboo')
250
+
251
+ # Timeouts don't occur when dummy publishing
252
+ CharCountWorker.sleep_time = 3
253
+ ReverseWorker.sleep_time = 3
254
+ hash = make_call(publisher, 'fooboo', 2)
255
+ assert_response(hash['CharCount'], :message, cc_val)
256
+ assert_response(hash['CharCount2'], :message, cc_val)
257
+ assert_response(hash['Length'], :message, 6)
258
+ assert_response(hash['Length2'], :message, 6)
259
+ assert_response(hash['Reverse'], :message, 'ooboof')
260
+ assert_response(hash['Triple'], :message, 'fooboofooboofooboo')
261
+ CharCountWorker.sleep_time = nil
262
+ ReverseWorker.sleep_time = nil
263
+
264
+ CharCountWorker.do_exception = true
265
+ TripleWorker.do_exception = true
266
+ hash = make_call(publisher, 'fooboo', 2)
267
+ assert_exception(hash['CharCount'], :explicit_exception)
268
+ assert_exception(hash['CharCount2'], :default_exception)
269
+ assert_response(hash['Length'], :message, 6)
270
+ assert_response(hash['Length2'], :message, 6)
271
+ assert_response(hash['Reverse'], :message, 'ooboof')
272
+ assert_exception(hash['Triple'], :default_exception)
273
+ CharCountWorker.do_exception = false
274
+ TripleWorker.do_exception = false
275
+ end
276
+ end
277
+ end
278
+ end