ap4r 0.3.1 → 0.3.2

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