ap4r 0.3.1 → 0.3.2

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,9 +1,9 @@
1
1
  # Author:: Shunichi Shinohara
2
- # Copyright:: Copyright (c) 2006 Future System Consulting Corp.
2
+ # Copyright:: Copyright (c) 2007 Future Architect Inc.
3
3
  # Licence:: MIT Licence
4
4
 
5
- module ReliableMsg
6
- module MessageStore
5
+ module ReliableMsg #:nodoc:
6
+ module MessageStore #:nodoc:
7
7
  class Base
8
8
 
9
9
  cattr_accessor :use_mysql_extention
@@ -37,11 +37,15 @@ module ReliableMsg
37
37
  end
38
38
 
39
39
  if ReliableMsg::MessageStore::Base.use_mysql_extention
40
- class Mysql
40
+ class Mysql #:nodoc:
41
41
  alias original_query query
42
+
43
+ # In Ruby/MySQL, +query+ method does NOT care about a given block.
44
+ # To make it behave the same as MySQL/Ruby, this method adds iteration
45
+ # over query results.
42
46
  def query(q, &block)
43
47
  maybe_result = original_query(q, &block)
44
- puts "Mysql extention: query called by #{q}"
48
+ puts "Mysql extention: query called by #{q}" if $DEBUG
45
49
  puts "Mysql#query returns #{maybe_result}(class: #{maybe_result.class})." if $DEBUG
46
50
  return maybe_result unless block && maybe_result.kind_of?(Mysql::Result)
47
51
  begin
@@ -1,5 +1,5 @@
1
1
  # Author:: Shunichi Shinohara
2
- # Copyright:: Copyright (c) 2007 Future Architect Corp.
2
+ # Copyright:: Copyright (c) 2007 Future Architect Inc.
3
3
  # Licence:: MIT Licence
4
4
 
5
5
  require 'mongrel'
@@ -8,8 +8,10 @@ require 'cgi'
8
8
  require 'ap4r'
9
9
 
10
10
  module Ap4r
11
- module Mongrel
12
11
 
12
+ module Mongrel #:nodoc:
13
+
14
+ # Gather controls AP4R server.
13
15
  class Ap4rConfigurator < ::Mongrel::Configurator
14
16
 
15
17
  def stop(needs_restart=false)
@@ -39,6 +41,7 @@ module Ap4r
39
41
 
40
42
  # Implements a handler that can run AP4R.
41
43
  # * If the requested exact PATH_INFO exists as a file then serve it.
44
+ # + (Second, access server information or queue/topic API) NOT IMPLEMENTED.
42
45
  # * Finally, raise an exception.
43
46
  #
44
47
  # memo: want to use this handler to take information from AP4R server
@@ -47,11 +50,13 @@ module Ap4r
47
50
  # TODO not yet implemented 2007/04/09 by shino
48
51
  #
49
52
  class Ap4rHandler < ::Mongrel::HttpHandler
50
- STDERR.puts "CAUTION! This script is rather experimental."
51
53
  attr_reader :files
52
54
  @@file_only_methods = ["GET","HEAD"]
53
55
 
54
56
  def initialize(options)
57
+ # TODO: needs various modes for easy operations 2007/05/02 by shino
58
+ # e.g. not to initialize message store, not to start dispatchers, etc.
59
+
55
60
  # TODO what is "false" here? 2007/04/13 by shinohara
56
61
  @files = ::Mongrel::DirHandler.new(options[:docroot], false)
57
62
  @tick = Time.now
@@ -89,7 +94,7 @@ module Ap4r
89
94
  def reload!
90
95
  begin
91
96
  #TODO not implemented 2007/04/09 by shino
92
- raise "not yet implemented!."
97
+ raise "not yet implemented!"
93
98
  end
94
99
  end
95
100
 
@@ -86,8 +86,7 @@ module Ap4r::Mongrel
86
86
 
87
87
  valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
88
88
  valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
89
- # TODO: now not utilized, skip checking 2007/04/23 by shino
90
- #valid_dir? @docroot, "Path to docroot not valid: #@docroot"
89
+ valid_dir? @docroot, "Path to docroot not valid: #@docroot"
91
90
 
92
91
  return @valid
93
92
  end
@@ -1,8 +1,9 @@
1
1
  # Author:: Shunichi Shinohara
2
- # Copyright:: Copyright (c) 2006 Future System Consulting Corp.
2
+ # Copyright:: Copyright (c) 2007 Future Architect Inc.
3
3
  # Licence:: MIT Licence
4
4
 
5
5
  module ReliableMsg
6
+
6
7
  # The +MultiQueue+ is a kind of clients.
7
8
  # This offers two extentions to <tt>ReliableMsg::Queue</tt>
8
9
  # 1. specify multiple target queues, by as comma-separated queue names.
@@ -1,31 +1,84 @@
1
1
  # Author:: Shunichi Shinohara
2
- # Copyright:: Copyright (c) 2006 Future System Consulting Corp.
2
+ # Copyright:: Copyright (c) 2007 Future Architect Inc.
3
3
  # Licence:: MIT Licence
4
4
 
5
- require 'ap4r/message_store_ext'
6
- require 'ap4r/multi_queue'
7
- require 'ap4r/retention_history'
8
-
9
- require 'soap/wsdlDriver'
10
- require 'xmlrpc/client'
11
- require 'uri'
12
- require 'net/http'
13
-
14
5
  require 'active_support'
15
6
  require 'yaml'
16
7
  require 'thread'
17
8
  require 'pp'
18
9
 
19
10
  require 'uuid'
11
+ require 'reliable-msg'
20
12
 
21
- module ReliableMsg
22
- module LifecycleListener #:nodoc:
13
+ require 'ap4r/message_store_ext'
14
+ require 'ap4r/multi_queue'
15
+ require 'ap4r/retention_history'
16
+ require 'ap4r/dispatcher'
17
+ require 'ap4r/carrier'
18
+
19
+ module ReliableMsg #:nodoc:
20
+
21
+ # = Dynamic configuration with ERb
22
+ #
23
+ # Some times you would like to inject dynamic values into your configuration file.
24
+ # In these cases, you can mix ERb in with your YAML to code some logic, like:
25
+ #
26
+ # <% acl = [] %>
27
+ # <% for i in 1..100 %>
28
+ # <% acl << "192.168.0.#{i}" %>
29
+ # <% end %>
30
+ # acl: <%= acl.map{|ip| "allow #{ip}"}.join(' ')
31
+ #
32
+ class Config
23
33
 
34
+ alias :load_no_create_original :load_no_create
35
+ alias :load_or_create_original :load_or_create
36
+
37
+ #--
38
+ # TODO: should enhance YAML.load_documents instead of this method?, 2007/5/7 kato-k
39
+ def load_no_create
40
+ if File.exist?(@file)
41
+ @config= {}
42
+ File.open @file, "r" do |input|
43
+ YAML.load_documents(erb_render(input.read)) do |doc|
44
+ @config.merge! doc
45
+ end
46
+ end
47
+ true
48
+ end
49
+ end
50
+
51
+ #--
52
+ # TODO: should enhance YAML.load_documents instead of this method?, 2007/5/7 kato-k
53
+ def load_or_create
54
+ if File.exist?(@file)
55
+ @config= {}
56
+ File.open @file, "r" do |input|
57
+ YAML.load_documents(erb_render(input.read)) do |doc|
58
+ @config.merge! doc
59
+ end
60
+ end
61
+ @logger.info format(INFO_LOADED_CONFIG, @file)
62
+ else
63
+ @config = {
64
+ "store" => DEFAULT_STORE,
65
+ "drb" => DEFAULT_DRB
66
+ }
67
+ save
68
+ @logger.info format(INFO_CREATED_CONFIG, @file)
69
+ end
70
+ end
71
+
72
+ private
73
+ def erb_render(configuration_content)
74
+ ERB.new(configuration_content).result
75
+ end
24
76
  end
25
-
77
+
26
78
  class QueueManager
27
- # Gets a queue name which has the most stale message
28
- # in queues specified by +multi_queue+.
79
+
80
+ # Gets a queue name which has the most stale message.
81
+ # +multi_queue+ specifies the target queue names to search.
29
82
  def stale_queue multi_queue
30
83
  @store.stale_queue multi_queue
31
84
  end
@@ -37,7 +90,6 @@ module ReliableMsg
37
90
  # Hooks original initialize method to add lifecyle listeners.
38
91
  #--
39
92
  # TODO: Make dispatchers and carriers lifecyle listeners, 2006/09/01 shino
40
- # TODO: and separate them from QueueManager, 2006/09/01 shino
41
93
  def initialize options = nil #:notnew:
42
94
  initialize_original options
43
95
  @global_lock ||= Mutex.new
@@ -51,14 +103,25 @@ module ReliableMsg
51
103
  self.class.class_eval("attr_#{attr_mode} :#{iv_name}")
52
104
  end
53
105
 
106
+ # Starts reliable-msg server and something around it.
107
+ #
108
+ # Order is:
109
+ # 1. Original reliable-msg server (message store and druby).
110
+ # 2. Dispatchers
111
+ # 3. Carriors (if exists)
112
+ # These are Reversed in +stop+.
54
113
  def start
55
114
  begin
56
115
  @global_lock.synchronize do
57
116
  return if @@active == self
58
- @dispatch_targets = ''
59
117
  start_original
60
- start_dispatchers
61
- start_carriers
118
+
119
+ @dispatchers = ::Ap4r::Dispatchers.new(self, @config.dispatchers, @logger)
120
+ @dispatchers.start
121
+
122
+ @carriors = ::Ap4r::Carriers.new(self, @config.carriers, @logger, @dispatchers)
123
+ @carriors.start
124
+
62
125
  @lifecycle_listeners.each {|l| l.start }
63
126
  end
64
127
  rescue Exception => err
@@ -67,195 +130,18 @@ module ReliableMsg
67
130
  end
68
131
  end
69
132
 
133
+ # Stops reliable-msg server and something around it.
134
+ # See +start+ also.
70
135
  def stop
71
136
  @global_lock.synchronize do
72
137
  return unless @@active == self
73
138
  @lifecycle_listeners.each {|l| l.stop }
74
- stop_carriers
75
- stop_dispatchers
139
+ @carriors.stop
140
+ @dispatchers.stop
76
141
  stop_original
77
142
  end
78
143
  end
79
144
 
80
- def start_dispatchers
81
- begin
82
- return unless @config.dispatchers
83
- @logger.info{ "about to start dispatchers with config #{@config.dispatchers.to_yaml}" }
84
- @disps = ThreadGroup.new
85
- @config.dispatchers.each{ |conf|
86
- conf["threads"].to_i.times { |index|
87
- Thread.fork(@disps, conf["targets"], index){|group, targets, index|
88
- dispatch_loop(group, targets, index)
89
- }
90
- }
91
- @dispatch_targets.concat(conf["targets"]).concat(';')
92
- @logger.info{ "dispatch targets are : #{@dispatch_targets}" }
93
- }
94
- @logger.info "queue manager has forked dispatchers"
95
- rescue Exception => err
96
- @logger.warn{"Error in starting dipatchers #{err}"}
97
- @logger.warn{err.backtrace.join("\n")}
98
- raise err
99
- end
100
- end
101
-
102
- def dispatch_loop(group, targets, index)
103
- group.add Thread.current
104
- mq = MultiQueue.new targets
105
- @logger.info{ "start dispatcher: targets= #{mq} (index #{index})" }
106
- until Thread.current[:dying]
107
- sleep 0.1
108
- # @logger.debug{ "try dispatch #{mq} #{mq.name}" }
109
- # TODO: needs timeout?, 2006/10/16 shino
110
- begin
111
- mq.get{|m|
112
- @logger.debug{"dispatcher get message\n#{m.to_yaml}"} if m
113
- response =
114
- case m.headers[:dispatch_mode]
115
- when :HTTP
116
- dispatch_via_http(m)
117
- when :XMLRPC
118
- dispatch_via_xmlrpc(m)
119
- when :SOAP
120
- dispatch_via_soap(m)
121
- else
122
- raise "undefined dispatch mode #{m.headers[:mode]}"
123
- end
124
- @logger.debug{"dispatcher get response\n#{response.to_yaml}"}
125
- }
126
- rescue Exception => err
127
- @logger.warn("dispatch err #{err.inspect}")
128
- @logger.warn(err.backtrace.join("\n"))
129
- end
130
- end
131
- @logger.info{"end dispatcher #{mq} (index #{index})"}
132
- end
133
-
134
- # Dispatch via a HTTP protocol
135
- # Current implementation uses POST method, irrespectively options[:target_method]
136
- #
137
- # Determination of "success" is two fold
138
- # * status code should be 200, other codes (including 201-2xx) are treated as error
139
- # * when status code is 200, body should include a string "true"
140
- def dispatch_via_http(message)
141
- #TODO: Add some request headers e.g. User-Agent and Accept, 2006/10/12 shino
142
- #TODO: Now support POST only, 2006/10/12 shino
143
- response = Net::HTTP.post_form(URI.parse(message[:target_url]),
144
- message.object)
145
- @logger.debug{"response status [#{response.code} #{response.message}]"}
146
-
147
- #TODO: make the difinition of success variable, 2006/10/13 shino
148
- unless response.kind_of?(Net::HTTPOK)
149
- error_message = "HTTP Response FAILURE, status [#{response.code} #{response.message}]"
150
- @logger.error(error_message)
151
- @logger.info{response.to_yaml}
152
- #TODO: must create AP4R specific Exception class, 2006/10/12 shino
153
- raise StandardError.new(error_message)
154
- end
155
-
156
- unless response.body =~ /true/
157
- error_message = "HTTP Response FAILURE, status [#{response.code} #{response.message}], body [#{response.body}]"
158
- #TODO: Refactor error logging, 2006/10/13 shino
159
- @logger.error(error_message)
160
- @logger.info{response.to_yaml}
161
- #TODO: must create AP4R specific Exception class, 2006/10/12 shino
162
- raise StandardError.new(error_message)
163
- end
164
-
165
- response
166
- end
167
-
168
- def dispatch_via_xmlrpc(message)
169
- endpoint = message[:target_url]
170
- client = XMLRPC::Client.new2(endpoint)
171
- success, response = client.call2(message[:target_action], message.object)
172
- raise response unless success
173
- response
174
- end
175
-
176
- def dispatch_via_soap(message)
177
- @logger.debug{message[:target_url]}
178
- driver = SOAP::WSDLDriverFactory.new(message[:target_url]).create_rpc_driver
179
- @logger.debug{driver}
180
- driver.send(message[:target_action], message.object)
181
- end
182
-
183
- def stop_dispatchers
184
- @logger.info{"stop_dispatchers #{@disps}"}
185
- return unless @disps
186
- @disps.list.each {|d| d[:dying] = true}
187
- @disps.list.each {|d| d.join }
188
- end
189
-
190
- def start_carriers
191
- conf = @config.carriers
192
- return unless conf
193
-
194
- @logger.info{ "ready to start carrires with config #{conf.to_yaml}" }
195
- @carriers = ThreadGroup.new
196
- conf.each { |remote|
197
- remote["threads"].times { |index|
198
- Thread.fork(@carriers, remote, index){|group, remote, index|
199
- # TODO: refactor structure, 2006/10/06 shino
200
- group.add Thread.current
201
- @logger.info{ "starting a carrier (index #{index}) for the queue manager #{remote['source_uri']}" }
202
- uri = remote['source_uri']
203
- until Thread.current[:dying]
204
- begin
205
- sleep 0.1
206
- #TODO check :dying flag here and break, 2006/09/01 shino
207
- #TODO cache DRbObject if necessary, 2006/09/01 shino
208
- remote_qm = DRb::DRbObject.new_with_uri(uri)
209
- queue_name = remote_qm.stale_queue @dispatch_targets
210
- next unless queue_name
211
-
212
- @logger.debug{ "stale queue name : #{queue_name}" }
213
- q = ReliableMsg::Queue.new queue_name, :drb_uri => uri
214
- q.get { |m|
215
- unless m
216
- @logger.debug{ "carrier strikes at the air (T_T)" }
217
- next
218
- end
219
- # @logger.debug{ "carrier gets a message\n#{m.to_yaml}" }
220
-
221
- # TODO: decide the better one, and delete another, 2006/09/01 shino
222
- # TODO: or switchable implementation in versions, 2006/10/16 shino
223
-
224
- #version 1: use thread fork so queue manager use a different tx
225
- # TODO probably should have a thread as an instance variable or in a thread local, 2006/09/01 shino
226
- #Thread.fork(m) {|m|
227
- # local_queue = ReliableMsg::Queue.new queue_name
228
- # local_queue.put m.object
229
- #}.join
230
-
231
- #version 2: store tx and set nil, and resotre tx after putting a message
232
- begin
233
- tx = Thread.current[ReliableMsg::Client::THREAD_CURRENT_TX]
234
- Thread.current[ReliableMsg::Client::THREAD_CURRENT_TX] = nil
235
- # @logger.debug{ "before tx: #{tx}" }
236
- ReliableMsg::Queue.new(queue_name).put(m.object)
237
- ensure
238
- Thread.current[ReliableMsg::Client::THREAD_CURRENT_TX] = tx
239
- # @logger.debug{ "after tx: #{Thread.current[ReliableMsg::Client::THREAD_CURRENT_TX]}" }
240
- end
241
- }
242
- rescue Exception => ex
243
- @logger.warn "error in remote-get/local-put #{ex}\n#{ex.backtrace.join("\n\t")}\n"
244
- end
245
- end
246
- @logger.info{"ends a carrier (index #{index}) for the queue manager #{remote['uri']}"}
247
- }
248
- }
249
- }
250
- @logger.info{"queue manager has forked all carriers"}
251
- end
252
-
253
- def stop_carriers
254
- @logger.info{"stop_carriers #{@carriers}"}
255
- return unless @carriers
256
- @carriers.list.each{|d| d[:dying] = true}
257
- @carriers.list.each{|d| d.join }
258
- end
259
145
 
260
146
  end
261
147
  end
@@ -1,12 +1,12 @@
1
1
  # Author:: Shunichi Shinohara
2
- # Copyright:: Copyright (c) 2006 Future System Consulting Corp.
2
+ # Copyright:: Copyright (c) 2007 Future Architect Inc.
3
3
  # Licence:: MIT Licence
4
4
 
5
5
  require 'drb/drb'
6
6
 
7
- module ReliableMsg
7
+ module ReliableMsg #:nodoc:
8
8
  class QueueManager
9
- attr_reader :store, :transactions, :mutex, :config
9
+ attr_reader :store, :transactions, :mutex, :config, :dispatchers, :carriers
10
10
 
11
11
  # Accepts ruby code as a string, evaluates it on +self+,
12
12
  # and returns the result as a formatted string.
@@ -38,9 +38,16 @@ module ReliableMsg
38
38
  end
39
39
  alias e2i eval_to_inspect
40
40
 
41
+ # Checks queues are all "empty".
42
+ #
43
+ # "Empty" means no messages in transaction and
44
+ # all queues but <tt>$dlq</tt> are empty.
45
+ def no_active_message?
46
+ @transactions.size.zero? && @store.queues.all?{|(q, ms)| q == "$dlq" || ms.size.zero? }
47
+ end
41
48
  end
42
49
 
43
- module MessageStore
50
+ module MessageStore #:nodoc:
44
51
  class Base
45
52
  include DRbUndumped
46
53
  attr_reader :mutex, :queues, :topics, :cache
@@ -50,9 +57,10 @@ module ReliableMsg
50
57
  def activate
51
58
  activate_original
52
59
  @mutex.extend DRbUndumped
53
- # @queues.extend DRbUndumped
54
- # @topics.extend DRbUndumped
55
- # @cache.extend DRbUndumped
60
+ # TODO: queues/topics/cache should be DRbUndumped? 2007/06/06 by shino
61
+ # @queues.extend DRbUndumped
62
+ # @topics.extend DRbUndumped
63
+ # @cache.extend DRbUndumped
56
64
  end
57
65
 
58
66
  end