ap4r 0.3.6 → 0.3.7

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