qwirk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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