ap4r 0.3.6 → 0.3.7

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.
@@ -1,4 +1,6 @@
1
1
  == 0.3.x
2
+ * Fixed: qdb connection via PostgresPR etc.
3
+ Based on a patch from cypher256 <kashihara at gmail.com>
2
4
 
3
5
  === 0.3.6 (February 6, 2008)
4
6
  * Added: configuration to set HTTP timeout.
@@ -11,7 +11,9 @@ config/queues_ar.cfg
11
11
  config/queues_disk.cfg
12
12
  config/queues_mysql.cfg
13
13
  config/queues_pgsql.cfg
14
+ fresh_rakefile
14
15
  lib/ap4r.rb
16
+ lib/ap4r/async_helper.rb
15
17
  lib/ap4r/carrier.rb
16
18
  lib/ap4r/db/migrate/001_reliable_msg_queue_and_topic.rb
17
19
  lib/ap4r/dispatcher.rb
@@ -39,6 +41,8 @@ lib/ap4r/util/queue_client.rb
39
41
  lib/ap4r/version.rb
40
42
  lib/ap4r/xxx_create_table_for_saf.rb
41
43
  lib/ap4r/xxx_create_table_for_saf_to_postgresql.rb
44
+ lib/tasks/ap4r.rb
45
+ lib/tasks/databases.rake
42
46
  rails_plugin/ap4r/init.rb
43
47
  rails_plugin/ap4r/lib/ap4r/queue_put_stub.rb
44
48
  rails_plugin/ap4r/lib/ap4r/service_handler.rb
@@ -49,6 +53,7 @@ script/irm
49
53
  script/mongrel_ap4r
50
54
  script/start
51
55
  script/stop
56
+ spec/local/async_helper_queue_put_spec.rb
52
57
  spec/local/dispatcher_base_spec.rb
53
58
  spec/local/message_builder_spec.rb
54
59
  spec/local/send_message_handler_spec.rb
data/Rakefile CHANGED
@@ -18,7 +18,7 @@ begin
18
18
  p.email = %q{shinohara.shunichi@future.co.jp, kato.kiwamu@future.co.jp}
19
19
 
20
20
  p.extra_deps << ['reliable-msg', '=1.1.0']
21
- p.extra_deps << ['activesupport']
21
+ # p.extra_deps << ['activesupport']
22
22
  p.extra_deps << ['mongrel']
23
23
  p.extra_deps << ['rake']
24
24
  p.extra_deps << ['hoe']
@@ -151,26 +151,6 @@ task :stats => "release:copy_plugin" do
151
151
  ).to_s
152
152
  end
153
153
 
154
- namespace :qdb do
155
- desc "Make queue and topic tables through scripts in lib/ap4r/db/migrate."
156
- task :migrate do
157
-
158
- # Todo: configurable file name, 2007/10/01 kiwamu
159
- ap4r_config_file = "config/queues_ar.cfg"
160
- ap4r_config = YAML::load(ERB.new(IO.read(ap4r_config_file)).result)
161
- database_config = ap4r_config["store"]
162
- if "activerecord" == database_config["type"].downcase
163
- database_config["adapter"] = database_config["adapter"].downcase
164
- else
165
- # Todo
166
- end
167
- ActiveRecord::Base.logger = Logger.new(STDOUT)
168
- ActiveRecord::Base.establish_connection(database_config)
169
-
170
- ActiveRecord::Migrator.migrate("lib/ap4r/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
171
- end
172
- end
173
-
174
154
  todos_dirs = %w(lib rails_plugin spec)
175
155
 
176
156
  desc "List TODO comments in #{todos_dirs.join(", ")} directories"
@@ -1,12 +1,13 @@
1
- ---
2
- store:
1
+ ---
2
+ store:
3
3
  type: postgresql
4
- uri: # default is tcp://localhost:5432
4
+ host: localhost
5
+ port: 5432
5
6
  database: ap4r
6
7
  username: ap4r
7
8
  password: ap4r
8
- drb:
9
- host:
9
+ drb:
10
+ host:
10
11
  port: 6438
11
12
  acl: allow 127.0.0.1 allow ::1 allow 10.0.0.0/8
12
13
  dispatchers:
@@ -14,6 +15,6 @@ dispatchers:
14
15
  targets: queue.*
15
16
  threads: 1
16
17
  #carriers:
17
- # -
18
+ # -
18
19
  # source_uri: druby://another.host.local:6438
19
20
  # threads: 1
@@ -0,0 +1,10 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require 'rake'
5
+ require 'rake/testtask'
6
+ require 'rake/rdoctask'
7
+
8
+ require 'ap4r'
9
+ require 'tasks/ap4r'
10
+
@@ -0,0 +1,375 @@
1
+ # Author:: Shunichi Shinohara
2
+ # Copyright:: Copyright (c) 2007 Future Architect Inc.
3
+ # Licence:: MIT Licence
4
+
5
+ require 'reliable-msg'
6
+ require 'ap4r/stored_message'
7
+ require 'ap4r/message_builder'
8
+
9
+ module Ap4r
10
+
11
+ # This +AsyncHelper+ is included to +Ap4rClient+ and works the Rails plugin
12
+ # for asynchronous processing.
13
+ #
14
+ module AsyncHelper
15
+
16
+ module Base
17
+ Converters = {}
18
+
19
+ DRUBY_HOST = ENV['AP4R_DRUBY_HOST'] || 'localhost'
20
+ DRUBY_PORT = ENV['AP4R_DRUBY_PORT'] || '6438'
21
+ DRUBY_URI = "druby://#{DRUBY_HOST}:#{DRUBY_PORT}"
22
+
23
+ @@druby_uris = [DRUBY_URI]
24
+ @@druby_uri_options = { :rotate => false, :fail_over => false, :fail_reuse => false }
25
+ @@druby_uri_retry_count = 0
26
+ @@druby_uris_size = 1
27
+
28
+ @@default_dispatch_mode = :HTTP
29
+ @@default_rm_options = { :delivery => :once, :dispatch_mode => @@default_dispatch_mode }
30
+ @@default_queue_prefix = "queue."
31
+
32
+ mattr_accessor :default_dispatch_mode, :default_rm_options, :default_queue_prefix, :saf_delete_mode
33
+
34
+ def druby_uris(uris, options = {})
35
+ @@druby_uris = []
36
+ if uris.empty?
37
+ @@druby_uris << DRUBY_URI
38
+ else
39
+ @@druby_uris << uris
40
+ @@druby_uris.flatten!
41
+ end
42
+ @@druby_uri_options = @@druby_uri_options.merge(options)
43
+ @@druby_uris_size = @@druby_uris.size
44
+ @@druby_uri_retry_count = 0
45
+ end
46
+ module_function :druby_uris
47
+
48
+ # This method is aliased as ::Ap4r::Client#transaction
49
+ #
50
+ def transaction_with_saf(active_record_class = ::Ap4r::StoredMessage, *objects, &block)
51
+
52
+ Thread.current[:use_saf] = true
53
+ Thread.current[:stored_messages] = {}
54
+
55
+ # store
56
+ active_record_class ||= ::Ap4r::StoredMessage
57
+ active_record_class.transaction(*objects, &block)
58
+
59
+ # forward
60
+ forwarded_messages = {}
61
+ begin
62
+
63
+ # TODO: reconsider forwarding strategy, 2006/10/13 kato-k
64
+ # Once some error occured, such as disconnect reliable-msg or server crush,
65
+ # which is smart to keep to put a message or stop to do it?
66
+ # In the case of being many async messages, the former strategy is not so good.
67
+ #
68
+ # TODO: add delayed forward mode 2007/05/02 by shino
69
+ Thread.current[:stored_messages].each {|k,v|
70
+ __queue_put(v[:queue_name], v[:queue_message], v[:queue_headers])
71
+ forwarded_messages[k] = v
72
+ }
73
+ rescue Exception => err
74
+ # Don't raise any Exception. Application logic has already completed and messages are saved.
75
+ logger.warn("Failed to put a message into queue: #{err}")
76
+ end
77
+
78
+ begin
79
+ StoredMessage.transaction do
80
+ options = {:delete_mode => @@saf_delete_mode || :physical}
81
+ forwarded_messages.keys.each {|id|
82
+ ::Ap4r::StoredMessage.destroy_if_exists(id, options)
83
+ }
84
+ end
85
+ rescue Exception => err
86
+ # Don't raise any Exception. Application logic has already completed and messages are saved.
87
+ logger.warn("Failed to put a message into queue: #{err}")
88
+ end
89
+
90
+ ensure
91
+ Thread.current[:use_saf] = false
92
+ Thread.current[:stored_messages] = nil
93
+ end
94
+
95
+ # This method is aliased as ::Ap4r::Client#async_to
96
+ #
97
+ def async_dispatch(url_options = {}, async_params = {}, rm_options = {}, &block)
98
+
99
+ if logger.debug?
100
+ logger.debug("url_options: ")
101
+ logger.debug(url_options.inspect)
102
+ logger.debug("async_params: ")
103
+ logger.debug(async_params.inspect)
104
+ logger.debug("rm_options: ")
105
+ logger.debug(rm_options.inspect)
106
+ end
107
+
108
+ rm_options = @@default_rm_options.merge(rm_options || {})
109
+
110
+ # Only async_params is not cloned. options and rm_options are cloned before now.
111
+ # This is a current contract between this class and converter classes.
112
+ converter = Converters[rm_options[:dispatch_mode]].new(url_options, async_params, rm_options, self)
113
+ # logger.debug("druby uri for queue-manager : #{@@druby_uris.first}")
114
+
115
+ queue_name = converter.queue_name
116
+ queue_message = converter.make_params
117
+ queue_headers = converter.make_rm_options
118
+
119
+ message_builder = ::Ap4r::MessageBuilder.new(queue_name, queue_message, queue_headers)
120
+ if block_given?
121
+ message_builder.instance_eval(&block)
122
+ end
123
+ queue_name = message_builder.queue_name
124
+ queue_headers = message_builder.message_headers
125
+ # TODO: proces flow of Converter and MessageBuilder should (probably) be reversed 2007/09/19 by shino
126
+ # This branching is ad-hoc fix
127
+ if queue_headers[:dispatch_mode] == :HTTP
128
+ queue_message = message_builder.format_message_body
129
+ else
130
+ queue_message = message_builder.message_body
131
+ end
132
+
133
+
134
+ if Thread.current[:use_saf]
135
+ stored_message = ::Ap4r::StoredMessage.store(queue_name, queue_message, queue_headers)
136
+
137
+ Thread.current[:stored_messages].store(
138
+ stored_message.id,
139
+ {
140
+ :queue_message => queue_message,
141
+ :queue_name => queue_name,
142
+ :queue_headers => queue_headers
143
+ } )
144
+ return stored_message.id
145
+ end
146
+
147
+ __queue_put(queue_name, queue_message, queue_headers)
148
+ end
149
+
150
+ private
151
+ def __queue_put(queue_name, queue_message, queue_headers)
152
+ # TODO: can use a Queue instance repeatedly? 2007/05/02 by shino
153
+ begin
154
+ druby_uri = @@druby_uris.first
155
+ logger.debug "Trying to connect to #{druby_uri} (druby uri list : #{@@druby_uris.join(", ")})."
156
+
157
+ q = ReliableMsg::Queue.new(queue_name, :drb_uri => druby_uri)
158
+ q.put(queue_message, queue_headers)
159
+
160
+ logger.debug "#{druby_uri} was connected and message was enqueued successfully."
161
+ @@druby_uri_retry_count = 0
162
+ __rotate_druby_uri if @@druby_uri_options[:rotate]
163
+ rescue DRb::DRbConnError => err
164
+ raise err unless @@druby_uri_options[:fail_over]
165
+ logger.debug "#{druby_uri} was not connected."
166
+
167
+ unconnected_uri = @@druby_uris.shift
168
+ raise "No more active druby uri." if @@druby_uris.empty?
169
+
170
+ @@druby_uris << unconnected_uri if @@druby_uri_options[:fail_reuse]
171
+ @@druby_uri_retry_count += 1
172
+
173
+ if @@druby_uri_retry_count >= @@druby_uris_size
174
+ @@druby_uri_retry_count = 0
175
+ logger.debug "All registered druby uri was not connected."
176
+ raise err
177
+ end
178
+
179
+ __queue_put(queue_name, queue_message, queue_headers)
180
+ end
181
+ end
182
+
183
+ def __rotate_druby_uri()
184
+ druby_uri = @@druby_uris.shift
185
+ @@druby_uris << druby_uri
186
+ end
187
+
188
+ end
189
+
190
+ module Converters #:nodoc:
191
+
192
+ # A base class for converter classes.
193
+ # Responsibilities of subclasses are as folows
194
+ # * by +make_params+, convert async_params to appropriate object
195
+ # * by +make_rm_options+, make appropriate +Hash+ passed by <tt>ReliableMsg::Queue#put</tt>
196
+ class Base
197
+
198
+ # Difine a constant +DISPATCH_MODE+ to value 'mode_symbol' and
199
+ # add self to a Converters list.
200
+ def self.dispatch_mode(mode_symbol)
201
+ self.const_set(:DISPATCH_MODE, mode_symbol)
202
+ ::Ap4r::AsyncHelper::Base::Converters[mode_symbol] = self
203
+ end
204
+
205
+ def initialize(url_options, async_params, rm_options, url_for_handler)
206
+ @url_options = url_options
207
+ @async_params = async_params
208
+ @rm_options = rm_options
209
+ @url_for_handler = url_for_handler
210
+ end
211
+
212
+ # Returns a queue name to which a message will be queued.
213
+ # Should be implemented by subclasses.
214
+ def queue_name
215
+ raise 'must be implemented in subclasses'
216
+ end
217
+
218
+ # Returns a object which passed to <tt>ReliableMsg::Queue.put(message, headers)</tt>'s
219
+ # first argument +message+.
220
+ # Should be implemented by subclasses.
221
+ def make_params
222
+ raise 'must be implemented in subclasses'
223
+ end
224
+
225
+ # Returns a object which passed to <tt>ReliableMsg::Queue.put(message, headers)</tt>'s
226
+ # second argument +headers+.
227
+ # Should be implemented by subclasses.
228
+ def make_rm_options
229
+ raise 'must be implemented in subclasses'
230
+ end
231
+
232
+ private
233
+ # helper method for <tt>ActionController#url_for</tt>
234
+ def url_for(url_for_options, *parameter_for_method_reference)
235
+ return url_for_options if url_for_options.kind_of?(String)
236
+ @url_for_handler.url_for(url_for_options, *parameter_for_method_reference)
237
+ end
238
+
239
+ end
240
+
241
+ class ToRailsBase < Base
242
+ def initialize(url_options, async_params, rm_options, url_for_handler)
243
+ super
244
+
245
+ @url_options ||= {}
246
+ @url_options[:controller] ||= @url_for_handler.controller_path.gsub("/", ".")
247
+ @url_options[:url] ||= {:controller => url_options[:controller], :action => url_options[:action]}
248
+ @url_options[:url][:controller] ||= url_options[:controller] if url_options[:url].kind_of?(Hash)
249
+ end
250
+
251
+ def queue_name
252
+ queue_name = @rm_options[:queue]
253
+ return queue_name if queue_name
254
+
255
+ queue_prefix = ::Ap4r::AsyncHelper::Base.default_queue_prefix
256
+ queue_prefix = queue_prefix.chomp(".")
257
+ url = @url_options[:url]
258
+ if url.kind_of?(Hash)
259
+ @rm_options[:queue] ||=
260
+ [queue_prefix, url[:controller].to_s, url[:action].to_s].join(".")
261
+ else
262
+ @rm_options[:queue] ||=
263
+ "#{queue_prefix}.#{URI.parse(url).path.gsub("/", ".")}"
264
+ end
265
+ @rm_options[:queue]
266
+ end
267
+ end
268
+
269
+ class Http < ToRailsBase
270
+ dispatch_mode :HTTP
271
+
272
+ def make_params
273
+ @async_params
274
+ end
275
+
276
+ def make_rm_options
277
+ @rm_options[:target_url] ||= url_for(@url_options[:url])
278
+ @rm_options[:target_method] ||= 'POST'
279
+ #TODO: make option key to specify HTTP headers, 2006/10/16 shino
280
+ @rm_options
281
+ end
282
+ end
283
+
284
+ class WebService < ToRailsBase
285
+ def make_params
286
+ message_obj = {}
287
+ @async_params.each_pair{|k,v| message_obj[k.to_sym]=v}
288
+ message_obj
289
+ end
290
+
291
+ def make_rm_options
292
+ @rm_options[:target_url] ||= target_url_name
293
+ @rm_options[:target_action] ||= action_api_name
294
+ @rm_options
295
+ end
296
+
297
+ def action_api_name
298
+ action_method_name = @url_options[:url][:action]
299
+ action_method_name.camelcase
300
+ end
301
+
302
+ def options_without_action
303
+ @url_options[:url].reject{ |k,v| k == :action }
304
+ end
305
+
306
+ end
307
+
308
+ class XmlRpc < WebService
309
+ dispatch_mode :XMLRPC
310
+
311
+ def target_url_name
312
+ url_for(options_without_action) + rails_api_url_suffix
313
+ end
314
+
315
+ private
316
+ def rails_api_url_suffix
317
+ '/api'
318
+ end
319
+ end
320
+
321
+ class SOAP < WebService
322
+ dispatch_mode :SOAP
323
+
324
+ def target_url_name
325
+ url_for(options_without_action) + rails_wsdl_url_suffix
326
+ end
327
+
328
+ private
329
+ def rails_wsdl_url_suffix
330
+ '/service.wsdl'
331
+ end
332
+ end
333
+
334
+ class Druby < Base
335
+ OPTION_KEY = :receiver
336
+ dispatch_mode :druby
337
+
338
+ @@default_url = "druby://localhost:9999"
339
+ cattr_accessor :default_url
340
+
341
+ def initialize(url_options, async_params, rm_options, url_for_handler)
342
+ super
343
+ @url_options[:url] ||= @@default_url
344
+ end
345
+
346
+ def queue_name
347
+ queue_name = @rm_options[:queue]
348
+ return queue_name if queue_name
349
+
350
+ @rm_options[:queue] =
351
+ [AsyncHelper::Base.default_queue_prefix.chomp("."),
352
+ @url_options[OPTION_KEY].to_s || "druby",
353
+ @url_options[:message].to_s].join(".")
354
+ @rm_options[:queue]
355
+ end
356
+
357
+ def make_params
358
+ @async_params
359
+ end
360
+
361
+ def make_rm_options
362
+ @rm_options[:target_url] ||=
363
+ if @url_options[OPTION_KEY]
364
+ "#{@url_options[:url]}?#{@url_options[OPTION_KEY]}"
365
+ else
366
+ @url_options[:url]
367
+ end
368
+ @rm_options[:target_method] = @url_options[:message]
369
+ @rm_options
370
+ end
371
+ end
372
+
373
+ end
374
+ end
375
+ end
@@ -21,9 +21,10 @@ module Ap4r
21
21
  # - calls a <tt>Dispatchers::Base</tt>'s instance.
22
22
  class Dispatchers
23
23
 
24
+ attr_reader :config, :group
24
25
  @@sleep_inverval = 0.1
25
-
26
26
  @@logger = nil
27
+
27
28
  def self.logger
28
29
  @@logger
29
30
  end
@@ -497,7 +497,7 @@ if Object.const_defined? :PostgresPR
497
497
  maybe_result = original_query(q, &block)
498
498
  puts "PostgresPR: query called by #{q}" if $DEBUG
499
499
  puts "PostgresPR::Connenction#query returns #{maybe_result}(class: #{maybe_result.class})." if $DEBUG
500
- return maybe_result.rows unless block && maybe_result.kind_of?(PostgresPR::Connection::Result)
500
+ return maybe_result unless block && maybe_result.kind_of?(PostgresPR::Connection::Result)
501
501
  begin
502
502
  puts "PostgresPR extention: about to yield result." if $DEBUG
503
503
  block.call(maybe_result.rows)
@@ -291,5 +291,88 @@ module Ap4r
291
291
 
292
292
  end
293
293
 
294
+ # This class is an experimental implementation of monitoring API by HTTP.
295
+ # It's possible to get the number of message in an arbitrary queue and
296
+ # the number of (alive/dead) thread of dispatchers.
297
+ #
298
+ # === Rrequest example ===
299
+ # GET /mointoring/queues/queue.test HTTP/1.1
300
+ # GET /mointoring/queues/all HTTP/1.1
301
+ # GET /mointoring/queues/dlq HTTP/1.1
302
+ #
303
+ # GET /mointoring/dispatchers/alive_threads HTTP/1.1
304
+ # GET /mointoring/dispatchers/dead_threads HTTP/1.1
305
+ #
306
+ class Ap4rMonitoringHandler < ::Mongrel::HttpHandler
307
+
308
+ def initialize(options)
309
+ @tick = Time.now
310
+
311
+ dlq = ReliableMsg::Queue.new "$dlq"
312
+ @qm = dlq.send :qm
313
+ end
314
+
315
+ def process(request, response)
316
+ if response.socket.closed?
317
+ return
318
+ end
319
+
320
+ target = request.params[::Mongrel::Const::PATH_INFO][1..-1]
321
+
322
+ if "GET".include? request.params[::Mongrel::Const::REQUEST_METHOD]
323
+ begin
324
+ # TODO: consider URL for each target, 2008/02/28 by kiwamu
325
+ result = case target
326
+ when /^queues\/*(\S*)/
327
+ case queue_name = $1
328
+ when ""
329
+ @qm.store.queues.keys.join(" ")
330
+ when "dlq"
331
+ @qm.store.queues["$dlq"].size
332
+ when "all"
333
+ @qm.store.queues.map{|k,v| v.size}.sum
334
+ else
335
+ @qm.store.queues[queue_name].size
336
+ end
337
+ when /^dispatchers\/*(\S*)/
338
+ case $1
339
+ when "alive_threads"
340
+ @qm.dispatchers.group.list.size
341
+ when "dead_threads"
342
+ diff = @qm.dispatchers.config.map{|d| d["threads"]}.sum - @qm.dispatchers.group.list.size
343
+ diff > 0 ? diff : 0
344
+ else
345
+ raise
346
+ end
347
+ else
348
+ raise
349
+ end
350
+
351
+ response.start(200) do |head, out|
352
+ head['Content-Type'] = 'text/plain'
353
+ out.write result
354
+ end
355
+ rescue
356
+ response.start(500) do |head, out|
357
+ head['Content-Type'] = 'text/plain'
358
+ out.write "Failed to monitor #{target}"
359
+ end
360
+ end
361
+ else
362
+ raise "HTTP method is not GET..."
363
+ end
364
+ end
365
+
366
+ # Does the internal reload for Rails. It might work for most cases, but
367
+ # sometimes you get exceptions. In that case just do a real restart.
368
+ def reload!
369
+ begin
370
+ #TODO not implemented 2007/04/09 by shino
371
+ raise "not yet implemented!"
372
+ end
373
+ end
374
+
375
+ end
376
+
294
377
  end
295
378
  end
@@ -118,6 +118,7 @@ module Ap4r::Mongrel
118
118
  uri "/", :handler => ::Ap4r::Mongrel::Ap4rHandler.new(defaults)
119
119
  uri "/queues", :handler => ::Ap4r::Mongrel::Ap4rSendMessageHandler.new(defaults)
120
120
  uri "/subscribes", :handler => ::Ap4r::Mongrel::Ap4rSubscribeMessageHandler.new(defaults)
121
+ uri "/monitoring", :handler => ::Ap4r::Mongrel::Ap4rMonitoringHandler.new(defaults)
121
122
  end
122
123
  setup_signals(settings)
123
124
  end
@@ -9,11 +9,11 @@ module Ap4r
9
9
  module Script
10
10
  class WorkspaceGenerator < Base
11
11
  AP4R_Directories = %w(config log public script tmp)
12
-
12
+
13
13
  def run argv, options
14
14
  OptionParser.new {|opt|
15
15
  opt.on('-m'){
16
- # merge to rails project but not implemented yet...
16
+ # merge to rails project but not implemented yet...
17
17
  }
18
18
 
19
19
  opt.parse!(argv)
@@ -38,6 +38,8 @@ module Ap4r
38
38
  File.join(root_dir, recursive_copy_dir))
39
39
  }
40
40
 
41
+ copy_file(File.join(ap4r_base, "fresh_rakefile"), File.join(root_dir, "Rakefile"))
42
+
41
43
  logger.info{"\n[#{root_dir}] has successfully set up!\n"}
42
44
 
43
45
  end
@@ -58,6 +60,11 @@ module Ap4r
58
60
  }
59
61
  end
60
62
 
63
+ def copy_file(src, dest)
64
+ FileUtils.cp(src, dest)
65
+ logger.info{"copy file from #{File.expand_path(src)} to #{dest} ..."}
66
+ end
67
+
61
68
  end
62
69
  end
63
70
  end
@@ -8,7 +8,7 @@ module Ap4r
8
8
  module VERSION #:nodoc:
9
9
  MAJOR = 0
10
10
  MINOR = 3
11
- TINY = 6
11
+ TINY = 7
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY].join('.')
14
14
  end
@@ -0,0 +1,7 @@
1
+ $VERBOSE = nil
2
+
3
+ # Load AP4R rakefile extensions
4
+ Dir["#{File.dirname(__FILE__)}/*.rake"].each { |ext| load ext }
5
+
6
+ # Load any custom rakefile extensions
7
+ #Dir["#{AP4R_ROOT}/lib/tasks/**/*.rake"].sort.each { |ext| load ext }
@@ -0,0 +1,37 @@
1
+ require 'rubygems'
2
+ require 'erb'
3
+ require 'find'
4
+ require 'active_record'
5
+ require File.join(File.dirname(__FILE__), '/../ap4r/version')
6
+
7
+ namespace :qdb do
8
+ desc "Make queue and topic tables through scripts in lib/ap4r/db/migrate."
9
+ task :migrate do
10
+
11
+ # Todo: configurable file name, 2007/10/01 kiwamu
12
+ ap4r_config_file = "config/queues_ar.cfg"
13
+ ap4r_config = YAML::load(ERB.new(IO.read(ap4r_config_file)).result)
14
+ database_config = ap4r_config["store"]
15
+ if "activerecord" == database_config["type"].downcase
16
+ database_config["adapter"] = database_config["adapter"].downcase
17
+ else
18
+ # Todo
19
+ end
20
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
21
+ ActiveRecord::Base.establish_connection(database_config)
22
+
23
+ ActiveRecord::Migrator.migrate("lib/ap4r/db/migrate/", ENV["VERSION"] ? ENV["VERSION"].to_i : nil)
24
+ end
25
+ end
26
+
27
+
28
+ namespace :db do
29
+ desc "Create table for persistent message."
30
+ task :create do
31
+
32
+
33
+
34
+
35
+ end
36
+ end
37
+
@@ -1,4 +1,6 @@
1
+ puts "---"
1
2
  require 'rubygems'
3
+ puts "==="
2
4
  require 'ap4r'
3
-
5
+ puts "==="
4
6
  load 'ap4r/mongrel_ap4r.rb'
@@ -0,0 +1,418 @@
1
+ require File.join(File.dirname(__FILE__), "../spec_helper")
2
+
3
+ require "reliable-msg"
4
+ require "ap4r/async_helper"
5
+
6
+ module Ap4r::AsyncHelper::Base
7
+ module_function :__queue_put
8
+ end
9
+
10
+ class Target
11
+ include Ap4r::AsyncHelper::Base
12
+ def initialize
13
+ @@logger = Logger.new(STDOUT)
14
+ @@logger.level = Logger::INFO
15
+ end
16
+
17
+ def queue_put
18
+ __queue_put("", { }, { })
19
+ end
20
+
21
+ require "active_support"
22
+ cattr_accessor :logger, :druby_uris, :druby_uri_retry_count
23
+ end
24
+
25
+ # mock
26
+ class ReliableMsg::Queue
27
+ def initialize(queue_name, options = { })
28
+ @druby_uri = options[:drb_uri]
29
+ end
30
+
31
+ def put(queue_message, queue_headers)
32
+ case @@ap4r_servers_status[@druby_uri]
33
+ when :active then "cd6f82d0-e9f1-012b-6ff8-0016cb9ad524" # uuid
34
+ when :inactive then raise ::DRb::DRbConnError
35
+ end
36
+ end
37
+
38
+ require "active_support"
39
+ cattr_accessor :ap4r_servers_status
40
+ end
41
+
42
+
43
+ describe Ap4r::AsyncHelper::Base, "no configuration and active server" do
44
+
45
+ before(:each) do
46
+ # status
47
+ ReliableMsg::Queue.ap4r_servers_status = {
48
+ "druby://localhost:6438" => :active
49
+ }
50
+ @target = Target.new
51
+ end
52
+
53
+ it "should have default druby uri" do
54
+ @target.druby_uris.size.should == 1
55
+ @target.druby_uris.first.should == "druby://localhost:6438"
56
+ end
57
+
58
+ it "should be not error" do
59
+ proc{ @target.queue_put }.should_not raise_error
60
+ end
61
+ end
62
+
63
+
64
+ describe Ap4r::AsyncHelper::Base, "no configuration and inactive server" do
65
+
66
+ before(:each) do
67
+ # status
68
+ ReliableMsg::Queue.ap4r_servers_status = {
69
+ "druby://localhost:6438" => :inactive
70
+ }
71
+ @target = Target.new
72
+ end
73
+
74
+ it "should be not error" do
75
+ proc{ @target.queue_put }.should raise_error(::DRb::DRbConnError)
76
+ end
77
+ end
78
+
79
+
80
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with default options and active server" do
81
+
82
+ before(:each) do
83
+ # configuration
84
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
85
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris)
86
+
87
+ # status
88
+ ReliableMsg::Queue.ap4r_servers_status = {
89
+ "druby://localhost:6438" => :active,
90
+ "druby://localhost:6439" => :active,
91
+ "druby://localhost:6440" => :active
92
+ }
93
+
94
+ @target = Target.new
95
+ end
96
+
97
+ it "should have configured URIs list druby uri" do
98
+ @target.druby_uris.size.should == @uris.size
99
+ @target.druby_uris.should == @uris
100
+ end
101
+
102
+ it "should be not error" do
103
+ proc{ @target.queue_put }.should_not raise_error
104
+ @target.druby_uris.first.should == @uris.first
105
+ end
106
+ end
107
+
108
+
109
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with default options and inactive server" do
110
+
111
+ before(:each) do
112
+ # configuration
113
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
114
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris)
115
+
116
+ # status
117
+ ReliableMsg::Queue.ap4r_servers_status = {
118
+ "druby://localhost:6438" => :inactive,
119
+ "druby://localhost:6439" => :active,
120
+ "druby://localhost:6440" => :active
121
+ }
122
+
123
+ @target = Target.new
124
+ end
125
+
126
+ it "should be error (should not fail over)" do
127
+ @target.druby_uris.first.should == @uris.first
128
+ proc{ @target.queue_put }.should raise_error(::DRb::DRbConnError)
129
+ @target.druby_uris.first.should == @uris.first
130
+ end
131
+ end
132
+
133
+
134
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with rotate option and active server" do
135
+
136
+ before(:each) do
137
+ # configuration
138
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
139
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => true, :fail_over => false, :fail_reuse => false)
140
+
141
+ # status
142
+ ReliableMsg::Queue.ap4r_servers_status = {
143
+ "druby://localhost:6438" => :active,
144
+ "druby://localhost:6439" => :active,
145
+ "druby://localhost:6440" => :active
146
+ }
147
+
148
+ @target = Target.new
149
+ end
150
+
151
+ it "should rotate servers each connection" do
152
+ @target.druby_uris.first.should == @uris[0]
153
+ proc{ @target.queue_put }.should_not raise_error
154
+
155
+ @target.druby_uris.first.should == @uris[1]
156
+ proc{ @target.queue_put }.should_not raise_error
157
+
158
+ @target.druby_uris.first.should == @uris[2]
159
+ proc{ @target.queue_put }.should_not raise_error
160
+
161
+ @target.druby_uris.first.should == @uris[0]
162
+ proc{ @target.queue_put }.should_not raise_error
163
+ end
164
+
165
+ it "should have the same URIs list after connections" do
166
+ 2.times{ @target.queue_put }
167
+ @target.druby_uris.size.should == 3
168
+ (@target.druby_uris - @uris).should == []
169
+ end
170
+ end
171
+
172
+
173
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with rotate option and inactive 2nd server" do
174
+
175
+ before(:each) do
176
+ # configuration
177
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
178
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => true, :fail_over => false, :fail_reuse => false)
179
+
180
+ # status
181
+ ReliableMsg::Queue.ap4r_servers_status = {
182
+ "druby://localhost:6438" => :active,
183
+ "druby://localhost:6439" => :inactive,
184
+ "druby://localhost:6440" => :active
185
+ }
186
+
187
+ @target = Target.new
188
+ end
189
+
190
+ it "should be error on the 2nd connection" do
191
+ @target.druby_uris.first.should == @uris[0]
192
+ proc{ @target.queue_put }.should_not raise_error
193
+
194
+ @target.druby_uris.first.should == @uris[1]
195
+ proc{ @target.queue_put }.should raise_error(::DRb::DRbConnError)
196
+
197
+ @target.druby_uris.first.should == @uris[1]
198
+ end
199
+
200
+ it "should be always error once error occured" do
201
+ @target.druby_uris.first.should == @uris[0]
202
+ proc{ @target.queue_put }.should_not raise_error
203
+
204
+ @target.druby_uris.first.should == @uris[1]
205
+ proc{ @target.queue_put }.should raise_error(::DRb::DRbConnError)
206
+
207
+ @target.druby_uris.first.should == @uris[1]
208
+ proc{ @target.queue_put }.should raise_error(::DRb::DRbConnError)
209
+ end
210
+ end
211
+
212
+
213
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with failover option and inactive 1st server" do
214
+
215
+ before(:each) do
216
+ # configuration
217
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
218
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => false, :fail_over => true, :fail_reuse => false)
219
+
220
+ # status
221
+ ReliableMsg::Queue.ap4r_servers_status = {
222
+ "druby://localhost:6438" => :inactive,
223
+ "druby://localhost:6439" => :active,
224
+ "druby://localhost:6440" => :active
225
+ }
226
+
227
+ @target = Target.new
228
+ end
229
+
230
+ it "should fail over to 2nd server" do
231
+ @target.druby_uris.first.should == @uris[0]
232
+ proc{ @target.queue_put }.should_not raise_error
233
+
234
+ @target.druby_uris.first.should == @uris[1]
235
+ proc{ @target.queue_put }.should_not raise_error
236
+
237
+ @target.druby_uris.first.should == @uris[1]
238
+ @target.druby_uris.size.should == 2
239
+ end
240
+ end
241
+
242
+
243
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with failover option and inactive 1st & 2nd servers" do
244
+
245
+ before(:each) do
246
+ # configuration
247
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
248
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => false, :fail_over => true, :fail_reuse => false)
249
+
250
+ # status
251
+ ReliableMsg::Queue.ap4r_servers_status = {
252
+ "druby://localhost:6438" => :inactive,
253
+ "druby://localhost:6439" => :inactive,
254
+ "druby://localhost:6440" => :active
255
+ }
256
+
257
+ @target = Target.new
258
+ end
259
+
260
+ it "should fail over to 3rd server" do
261
+ @target.druby_uris.first.should == @uris[0]
262
+ proc{ @target.queue_put }.should_not raise_error
263
+
264
+ @target.druby_uris.first.should == @uris[2]
265
+ proc{ @target.queue_put }.should_not raise_error
266
+
267
+ @target.druby_uris.first.should == @uris[2]
268
+ @target.druby_uris.size.should == 1
269
+ end
270
+ end
271
+
272
+
273
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with failover option and all inactive servers" do
274
+
275
+ before(:each) do
276
+ # configuration
277
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
278
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => false, :fail_over => true, :fail_reuse => false)
279
+
280
+ # status
281
+ ReliableMsg::Queue.ap4r_servers_status = {
282
+ "druby://localhost:6438" => :inactive,
283
+ "druby://localhost:6439" => :inactive,
284
+ "druby://localhost:6440" => :inactive
285
+ }
286
+
287
+ @target = Target.new
288
+ end
289
+
290
+ it "should be error" do
291
+ @target.druby_uris.first.should == @uris[0]
292
+ proc{ @target.queue_put }.should raise_error(::RuntimeError, "No more active druby uri.")
293
+
294
+ @target.druby_uris.size.should == 0
295
+ end
296
+ end
297
+
298
+
299
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with fail_reuse option and inactive 1st server" do
300
+
301
+ before(:each) do
302
+ # configuration
303
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
304
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => false, :fail_over => true, :fail_reuse => true)
305
+
306
+ # status
307
+ ReliableMsg::Queue.ap4r_servers_status = {
308
+ "druby://localhost:6438" => :inactive,
309
+ "druby://localhost:6439" => :active,
310
+ "druby://localhost:6440" => :active
311
+ }
312
+
313
+ @target = Target.new
314
+ end
315
+
316
+ it "should have the same URIs list after fail over" do
317
+ @target.druby_uri_retry_count.should == 0
318
+ @target.druby_uris.first.should == @uris[0]
319
+ proc{ @target.queue_put }.should_not raise_error
320
+ @target.druby_uri_retry_count.should == 0
321
+ @target.druby_uris.first.should == @uris[1]
322
+
323
+ @target.druby_uris.size.should == @uris.size
324
+ (@target.druby_uris - @uris).should == []
325
+ end
326
+ end
327
+
328
+
329
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with fail_reuse option and all inactive servers" do
330
+
331
+ before(:each) do
332
+ # configuration
333
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
334
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => false, :fail_over => true, :fail_reuse => true)
335
+
336
+ # status
337
+ ReliableMsg::Queue.ap4r_servers_status = {
338
+ "druby://localhost:6438" => :inactive,
339
+ "druby://localhost:6439" => :inactive,
340
+ "druby://localhost:6440" => :inactive
341
+ }
342
+
343
+ @target = Target.new
344
+ end
345
+
346
+ it "should be error after tryed all druby uri" do
347
+ @target.druby_uris.first.should == @uris[0]
348
+ proc{ @target.queue_put }.should raise_error(::RuntimeError)
349
+
350
+ @target.druby_uris.first.should == @uris[0]
351
+ @target.druby_uris.size.should == @uris.size
352
+ (@target.druby_uris - @uris).should == []
353
+ end
354
+
355
+ it "should be not error after some node comes back" do
356
+ proc{ @target.queue_put }.should raise_error(::RuntimeError)
357
+
358
+ ReliableMsg::Queue.ap4r_servers_status = {
359
+ "druby://localhost:6438" => :inactive,
360
+ "druby://localhost:6439" => :active,
361
+ "druby://localhost:6440" => :inactive
362
+ }
363
+
364
+ @target.druby_uris.first.should == @uris[0]
365
+ proc{ @target.queue_put }.should_not raise_error
366
+
367
+ @target.druby_uris.first.should == @uris[1]
368
+ end
369
+ end
370
+
371
+
372
+ describe Ap4r::AsyncHelper::Base, "multiple URIs with all options and inactive 2nd server" do
373
+
374
+ before(:each) do
375
+ # configuration
376
+ @uris = %w(6438 6439 6440).map {|port| "druby://localhost:#{port}"}
377
+ ::Ap4r::AsyncHelper::Base.druby_uris(@uris, :rotate => true, :fail_over => true, :fail_reuse => true)
378
+
379
+ # status
380
+ ReliableMsg::Queue.ap4r_servers_status = {
381
+ "druby://localhost:6438" => :active,
382
+ "druby://localhost:6439" => :inactive,
383
+ "druby://localhost:6440" => :active
384
+ }
385
+
386
+ @target = Target.new
387
+ end
388
+
389
+ it "should be not error on the first connection" do
390
+ proc { @target.queue_put }.should_not raise_error
391
+ end
392
+
393
+ it "should fail over on the second connection" do
394
+ @target.druby_uris.first.should == @uris[0]
395
+ @target.queue_put # connected to 6438
396
+
397
+ @target.druby_uris.first.should == @uris[1]
398
+ @target.queue_put # connected to 6440 and rotate next uri
399
+
400
+ @target.druby_uris.first.should == @uris[0]
401
+ end
402
+
403
+ it "should reuse the uri which it was inactive after it comes back" do
404
+ 2.times { @target.queue_put }
405
+ @target.druby_uris.first.should == @uris[0]
406
+
407
+ ReliableMsg::Queue.ap4r_servers_status = {
408
+ "druby://localhost:6438" => :active,
409
+ "druby://localhost:6439" => :active,
410
+ "druby://localhost:6440" => :active
411
+ }
412
+
413
+ @target.queue_put # connected to 6438
414
+ @target.queue_put # connected to 6439
415
+
416
+ @target.druby_uris.first.should == @uris[2]
417
+ end
418
+ end
@@ -96,7 +96,7 @@ describe "When block style API is used, " do
96
96
  end
97
97
 
98
98
  it "should return the urlencoded body." do
99
- @message_builder.format_message_body.should == "key1=value&key2=1"
99
+ @message_builder.format_message_body.split("&").sort.should == ["key2=1","key1=value"].sort
100
100
  end
101
101
 
102
102
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ap4r
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.6
4
+ version: 0.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shunichi Shinohara
@@ -10,11 +10,12 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2008-02-06 00:00:00 +09:00
13
+ date: 2009-03-19 00:00:00 +09:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: reliable-msg
18
+ type: :runtime
18
19
  version_requirement:
19
20
  version_requirements: !ruby/object:Gem::Requirement
20
21
  requirements:
@@ -22,17 +23,9 @@ dependencies:
22
23
  - !ruby/object:Gem::Version
23
24
  version: 1.1.0
24
25
  version:
25
- - !ruby/object:Gem::Dependency
26
- name: activesupport
27
- version_requirement:
28
- version_requirements: !ruby/object:Gem::Requirement
29
- requirements:
30
- - - ">="
31
- - !ruby/object:Gem::Version
32
- version: "0"
33
- version:
34
26
  - !ruby/object:Gem::Dependency
35
27
  name: mongrel
28
+ type: :runtime
36
29
  version_requirement:
37
30
  version_requirements: !ruby/object:Gem::Requirement
38
31
  requirements:
@@ -42,6 +35,7 @@ dependencies:
42
35
  version:
43
36
  - !ruby/object:Gem::Dependency
44
37
  name: rake
38
+ type: :runtime
45
39
  version_requirement:
46
40
  version_requirements: !ruby/object:Gem::Requirement
47
41
  requirements:
@@ -73,7 +67,9 @@ files:
73
67
  - config/queues_disk.cfg
74
68
  - config/queues_mysql.cfg
75
69
  - config/queues_pgsql.cfg
70
+ - fresh_rakefile
76
71
  - lib/ap4r.rb
72
+ - lib/ap4r/async_helper.rb
77
73
  - lib/ap4r/carrier.rb
78
74
  - lib/ap4r/db/migrate/001_reliable_msg_queue_and_topic.rb
79
75
  - lib/ap4r/dispatcher.rb
@@ -101,6 +97,8 @@ files:
101
97
  - lib/ap4r/version.rb
102
98
  - lib/ap4r/xxx_create_table_for_saf.rb
103
99
  - lib/ap4r/xxx_create_table_for_saf_to_postgresql.rb
100
+ - lib/tasks/ap4r.rb
101
+ - lib/tasks/databases.rake
104
102
  - rails_plugin/ap4r/init.rb
105
103
  - rails_plugin/ap4r/lib/ap4r/queue_put_stub.rb
106
104
  - rails_plugin/ap4r/lib/ap4r/service_handler.rb
@@ -111,6 +109,7 @@ files:
111
109
  - script/mongrel_ap4r
112
110
  - script/start
113
111
  - script/stop
112
+ - spec/local/async_helper_queue_put_spec.rb
114
113
  - spec/local/dispatcher_base_spec.rb
115
114
  - spec/local/message_builder_spec.rb
116
115
  - spec/local/send_message_handler_spec.rb
@@ -139,11 +138,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
138
  requirements: []
140
139
 
141
140
  rubyforge_project: ap4r
142
- rubygems_version: 1.0.0
141
+ rubygems_version: 1.3.1
143
142
  signing_key:
144
143
  specification_version: 2
145
144
  summary: Asynchronous Processing for Ruby.
146
145
  test_files:
146
+ - spec/local/async_helper_queue_put_spec.rb
147
147
  - spec/local/dispatcher_base_spec.rb
148
148
  - spec/local/message_builder_spec.rb
149
149
  - spec/local/send_message_handler_spec.rb