ap4r 0.3.0 → 0.3.1

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.
data/CHANGELOG CHANGED
@@ -1,9 +1,15 @@
1
1
  == 0.3.x
2
2
 
3
+ === 0.3.1 (April 24th, 2007)
4
+
5
+ * Changed: @delete_mode of AsyncController to @@saf_delete_mode with accessor
6
+ * Changed: default value of dispatch_mode, from :XMLRPC to :HTTP
7
+ * Added: Bootstrap script to let ap4r run on mongrel, experimental yet
8
+
3
9
  === 0.3.0 (April 6th, 2007)
4
10
 
5
- * Changed: Name space from "AP4R" to "Ap4r"
6
- * Added: Support the latest version for Rails(1.8.6) and RubyGems(0.9.2) and Rails(1.2.3)
11
+ * Changed: name space from "AP4R" to "Ap4r"
12
+ * Added: support the latest version for Rails(1.8.6) and RubyGems(0.9.2) and Rails(1.2.3)
7
13
 
8
14
  == 0.2.x
9
15
 
@@ -0,0 +1,99 @@
1
+ # Author:: Shunichi Shinohara
2
+ # Copyright:: Copyright (c) 2007 Future Architect Corp.
3
+ # Licence:: MIT Licence
4
+
5
+ require 'mongrel'
6
+ require 'cgi'
7
+
8
+ require 'ap4r'
9
+
10
+ module Ap4r
11
+ module Mongrel
12
+
13
+ class Ap4rConfigurator < ::Mongrel::Configurator
14
+
15
+ def stop(needs_restart=false)
16
+ ::ReliableMsg::Client.new.instance_eval do
17
+ qm.stop
18
+ end
19
+ super
20
+ join
21
+ end
22
+
23
+ def mswin?
24
+ RUBY_PLATFORM =~ /mswin/
25
+ end
26
+
27
+ def remove_pid_file
28
+ return unless @pid_file && File.exists?(@pid_file)
29
+ # TODO: slit exists between pid check and delete 2007/04/16 by shino
30
+ File.delete(@pid_file) if pid_from_file == Process.pid
31
+ end
32
+
33
+ def pid_from_file
34
+ File.open(@pid_file) do |file|
35
+ file.read.to_i
36
+ end
37
+ end
38
+ end
39
+
40
+ # Implements a handler that can run AP4R.
41
+ # * If the requested exact PATH_INFO exists as a file then serve it.
42
+ # * Finally, raise an exception.
43
+ #
44
+ # memo: want to use this handler to take information from AP4R server
45
+ # like mod_status. Message counts and status of threads are useful.
46
+ #
47
+ # TODO not yet implemented 2007/04/09 by shino
48
+ #
49
+ class Ap4rHandler < ::Mongrel::HttpHandler
50
+ STDERR.puts "CAUTION! This script is rather experimental."
51
+ attr_reader :files
52
+ @@file_only_methods = ["GET","HEAD"]
53
+
54
+ def initialize(options)
55
+ # TODO what is "false" here? 2007/04/13 by shinohara
56
+ @files = ::Mongrel::DirHandler.new(options[:docroot], false)
57
+ @tick = Time.now
58
+
59
+ # TODO: QueueManager life cycle should be controlled in Configurator? 2007/04/16 by shino
60
+ qm = ::ReliableMsg::QueueManager.new({:config => options[:ap4r_config_file]})
61
+ qm.start
62
+ end
63
+
64
+ # * If the requested exact PATH_INFO exists as a file then serve it.
65
+ # * Finally, raise an exception.
66
+ def process(request, response)
67
+ if response.socket.closed?
68
+ return
69
+ end
70
+
71
+ path_info = request.params[Mongrel::Const::PATH_INFO]
72
+ get_or_head = @@file_only_methods.include? request.params[Mongrel::Const::REQUEST_METHOD]
73
+ if get_or_head and @files.can_serve(path_info)
74
+ # File exists as-is so serve it up
75
+ @files.process(request,response)
76
+ else
77
+ raise "No file... Sorry" #TODO set 404 status 2007/04/09 by shino
78
+ end
79
+ end
80
+
81
+ def log_threads_waiting_for(event)
82
+ if Time.now - @tick > 10
83
+ @tick = Time.now
84
+ end
85
+ end
86
+
87
+ # Does the internal reload for Rails. It might work for most cases, but
88
+ # sometimes you get exceptions. In that case just do a real restart.
89
+ def reload!
90
+ begin
91
+ #TODO not implemented 2007/04/09 by shino
92
+ raise "not yet implemented!."
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
data/lib/ap4r/util/irm.rb CHANGED
@@ -1,4 +1,4 @@
1
- # Author:: Shunichi Shinohara
1
+ # Author:: Shunichi Shinohara
2
2
  # Copyright:: Copyright (c) 2006 Future System Consulting Corp.
3
3
  # Licence:: MIT Licence
4
4
 
data/lib/ap4r/version.rb CHANGED
@@ -8,7 +8,7 @@ module Ap4r
8
8
  module VERSION #:nodoc:
9
9
  MAJOR = 0
10
10
  MINOR = 3
11
- TINY = 0
11
+ TINY = 1
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY].join('.')
14
14
  end
@@ -0,0 +1,21 @@
1
+ # Author:: Kiwamu Kato
2
+ # Copyright:: Copyright (c) 2006 Future System Consulting Corp.
3
+ # Licence:: MIT Licence
4
+
5
+ class CreateTableForSaf < ActiveRecord::Migration
6
+ def self.up
7
+ create_table :stored_messages do |t|
8
+ t.column :duplication_check_id, :string, :null => false
9
+ t.column :queue, :string, :null => false
10
+ t.column :headers, :binary, :null => false
11
+ t.column :object, :binary, :null => false
12
+ t.column :status, :integer, :null => false
13
+ t.column :created_at, :datetime, :null => false
14
+ t.column :updated_at, :datetime, :null => false
15
+ end
16
+ end
17
+
18
+ def self.down
19
+ drop_table :stored_messages
20
+ end
21
+ end
@@ -6,51 +6,52 @@ require 'reliable-msg'
6
6
  #require 'ap4r/stored_message'
7
7
 
8
8
  module Ap4r
9
- # This +asyncController+ is the Rails plugin for asynchronous processing.
9
+
10
+ # This +AsyncController+ is the Rails plugin for asynchronous processing.
10
11
  # Asynchronous logics are called via various protocols, such as XML-RPC,
11
- # SOAP, HTTP PUT, and more. Now implemented jsut as XML-RPC.
12
+ # SOAP, HTTP POST, and more. Now default protocol is HTTP POST.
12
13
  #
13
14
  # Examples: The part of calling next asynchronous logics in a controller in the HelloWorld Sample.
14
15
  #
15
16
  # req = WorldRequest.new([:world_id => 1, :message => "World"})
16
17
  # async_dispatch(req,
17
- # {:controller => 'async_world', :action => 'execute',
18
- # :mode => :XMLRPC })
18
+ # {:controller => 'async_world', :action => 'execute'},
19
+ # :dispatch_mode => :XMLRPC }) # skippable
19
20
  #
20
21
  # render :action => 'response'
21
22
  #
22
23
  module AsyncController
23
-
24
+
24
25
  module Base
25
26
  Converters = {}
26
27
  DRUBY_URI = "druby://#{ENV['AP4R_DRUBY_HOST']||'localhost'}:#{ENV['AP4R_DRUBY_PORT'] || '6438'}"
27
28
 
28
- @@default_dispatch_mode = :XMLRPC
29
+ @@default_dispatch_mode = :HTTP
29
30
  @@default_rm_options = { :delivery => :once }
30
31
  @@default_queue_prefix = "queue."
31
32
 
32
- mattr_accessor :default_dispatch_mode, :default_rm_options, :default_queue_prefix
33
+ mattr_accessor :default_dispatch_mode, :default_rm_options, :default_queue_prefix, :saf_delete_mode
33
34
 
34
35
  # Provide at-least-once QoS level.
35
36
  # Execute application logic and store the message for next logic to the application
36
- # database. This is a store processing. After commit to the database,
37
+ # database. This is a store processing. After commit to the database,
37
38
  # put the message into queue and update or delte a management table.
38
39
  # This is a forward processing.
39
- # SAF(store and forward) processing like this guarantees that any message
40
- # is never lost and keeps reasonable performance.
41
- #
40
+ # SAF(store and forward) processing like this guarantees that any message
41
+ # is never lost and keeps reasonable performance.
42
+ #
42
43
  # Examples: Just call async_dispath method in this block.
43
44
  #
44
45
  # transaction_with saf do
45
46
  # req = WorldRequest.new([:world_id => 1, :message => "World"})
46
47
  # async_dispatch(req,
47
- # {:controller => 'async_world', :action => 'execute',
48
- # :mode => :XMLRPC })
48
+ # {:controller => 'async_world', :action => 'execute'},
49
+ # :dispatch_mode => :XMLRPC }) # skippable
49
50
  #
50
51
  # render :action => 'response'
51
52
  # end
52
53
  #
53
- def transaction_with_saf(active_record_class = StoredMessage, *objects, &block)
54
+ def transaction_with_saf(active_record_class = ::Ap4r::StoredMessage, *objects, &block)
54
55
 
55
56
  Thread.current[:use_saf] = true
56
57
  Thread.current[:stored_messages] = {}
@@ -72,25 +73,25 @@ module Ap4r
72
73
  forwarded_messages[k] = v
73
74
  }
74
75
  rescue Exception => err
75
- # Don't raise any Exception. Response to user already completed.
76
+ # Don't raise any Exception. Response to user already completed.
76
77
  # nop
77
78
  logger.warn("Failed to put a message into queue: #{err}")
78
79
  end
79
-
80
+
80
81
  begin
81
82
  StoredMessage.transaction do
82
- options = {:delete_mode => @delete_mode || :physical}
83
+ options = {:delete_mode => @@saf_delete_mode || :physical}
83
84
  forwarded_messages.keys.each {|id|
84
85
  ::Ap4r::StoredMessage.destroy_if_exists(id, options)
85
86
  }
86
87
  end
87
88
  rescue Exception => err
88
- # Don't raise any Exception. Response to user already completed.
89
+ # Don't raise any Exception. Response to user already completed.
89
90
  # nop
90
91
  logger.warn("Failed to put a message into queue: #{err}")
91
92
  end
92
-
93
- ensure
93
+
94
+ ensure
94
95
  Thread.current[:use_saf] = false
95
96
  Thread.current[:stored_messages] = nil
96
97
  end
@@ -126,6 +127,7 @@ module Ap4r
126
127
 
127
128
  # TODO: clone it, 2006/10/16 shino
128
129
  options ||= {}
130
+ options[:controller] ||= controller_path.gsub("/", ".")
129
131
  rm_options = @@default_rm_options.merge(rm_options || {})
130
132
  # TODO: put into :dispatch_mode to @@default_rm_options
131
133
  rm_options[:dispatch_mode] ||= @@default_dispatch_mode
@@ -143,14 +145,14 @@ module Ap4r
143
145
  stored_message = ::Ap4r::StoredMessage.store(queue_name, queue_message, queue_headers)
144
146
 
145
147
  Thread.current[:stored_messages].store(
146
- stored_message.id,
148
+ stored_message.id,
147
149
  {
148
- :queue_message => queue_message,
150
+ :queue_message => queue_message,
149
151
  :queue_name => queue_name,
150
- :queue_headers => queue_headers
152
+ :queue_headers => queue_headers
151
153
  } )
152
154
  return stored_message.id
153
- end
155
+ end
154
156
 
155
157
  __queue_put(queue_name, queue_message, queue_headers)
156
158
  end
@@ -162,8 +164,9 @@ module Ap4r
162
164
  end
163
165
 
164
166
  def __get_queue_name(options, rm_options)
165
- rm_options[:queue_name] ||
166
- @@default_queue_prefix.concat(options[:controller].to_s).concat('.').concat(options[:action].to_s)
167
+ rm_options[:queue_name] ||=
168
+ @@default_queue_prefix.clone.concat(options[:controller].to_s).concat('.').concat(options[:action].to_s)
169
+ rm_options[:queue_name]
167
170
  end
168
171
 
169
172
  end
@@ -238,7 +241,7 @@ module Ap4r
238
241
  @rm_options[:target_action] ||= action_api_name
239
242
  @rm_options
240
243
  end
241
-
244
+
242
245
  def action_api_name
243
246
  action_method_name = @options[:action]
244
247
  action_method_name.camelcase
@@ -249,7 +252,7 @@ module Ap4r
249
252
  new_opts[:action] = nil
250
253
  new_opts
251
254
  end
252
-
255
+
253
256
  end
254
257
 
255
258
  class XmlRpc < WebService
@@ -258,7 +261,7 @@ module Ap4r
258
261
  def target_url_name
259
262
  url_for(options_without_action) + rails_api_url_suffix
260
263
  end
261
-
264
+
262
265
  private
263
266
  def rails_api_url_suffix
264
267
  '/api'
@@ -0,0 +1,255 @@
1
+ # Author:: Shunichi Shinohara
2
+ # Copyright:: Copyright (c) 2007 Future Architect Corp.
3
+ # Licence:: MIT Licence
4
+
5
+ require 'rubygems'
6
+ require 'mongrel'
7
+ require 'ap4r/mongrel'
8
+
9
+ module Ap4r::Mongrel
10
+
11
+ module Command
12
+ PREFIX = "ap4r::mongrel::"
13
+
14
+ module Base
15
+ def self.included(klass)
16
+ return unless klass.kind_of?(Class)
17
+ klass.class_eval do |k|
18
+ include(::Mongrel::Command::Base)
19
+ end
20
+ # TODO: Can subclass the return of RubyGems::GemPlugin ? 2007/04/16 by shino
21
+ end
22
+
23
+ # send a signal to the process specified by pid_file.
24
+ def send_signal(signal, pid_file)
25
+ pid = open(pid_file).read.to_i
26
+ print "Send signal #{signal} to AP4R at PID #{pid} ..."
27
+ begin
28
+ Process.kill(signal, pid)
29
+ rescue Errno::ESRCH
30
+ puts "Process not found."
31
+ end
32
+ puts "Done."
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ class Help < GemPlugin::Plugin "/commands"
39
+ include ::Ap4r::Mongrel::Command::Base
40
+
41
+ def run
42
+ puts "Available AP4R commands are:\n\n"
43
+ Mongrel::Command::Registry.instance.commands.each do |name|
44
+ if name =~ /#{Command::PREFIX}/
45
+ name = name[Command::PREFIX.length .. -1]
46
+ end
47
+ puts " - #{name[1 .. -1]}\n"
48
+ end
49
+ puts "\nfor further help, run each command with -h option to get help."
50
+ end
51
+
52
+ end
53
+
54
+ class Version < GemPlugin::Plugin "/commands"
55
+ include ::Ap4r::Mongrel::Command::Base
56
+
57
+ def run
58
+ puts "AP4R Version is:"
59
+ puts
60
+ puts " - AP4R #{::Ap4r::VERSION::STRING}"
61
+ puts
62
+ end
63
+
64
+ end
65
+
66
+ class Start < GemPlugin::Plugin "/commands"
67
+ include ::Ap4r::Mongrel::Command::Base
68
+
69
+ def configure
70
+ options [
71
+ ["-d", "--daemonize", "Run in daemon mode", :@daemon, false],
72
+ ['-p', '--port PORT', "Port number used by mongrel", :@port, 7438],
73
+ ['-a', '--address HOST', "IP address used by mongrel", :@host, "0.0.0.0"],
74
+ ['-A', '--ap4r-config FILE', "Config file for reliable-msg/AP4R", :@ap4r_config_file, "config/queues.cfg"],
75
+ ['-l', '--log FILE', "Log file", :@log_file, "log/mongrel_ap4r.log"],
76
+ ['-P', '--pid FILE', "PID file", :@pid_file, "log/mongrel_ap4r.pid"],
77
+ ['-r', '--root PATH', "Document root (no meanings yet.)", :@docroot, "public"],
78
+ ['-c', '--chdir PATH', "Change to dir", :@cwd, Dir.pwd],
79
+ ]
80
+ end
81
+
82
+ def validate
83
+ @cwd = File.expand_path(@cwd)
84
+ valid_dir? @cwd, "Path of chdir not valid: #@cwd "
85
+ Dir.chdir(@cwd)
86
+
87
+ valid_dir? File.dirname(@log_file), "Path to log file not valid: #@log_file"
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"
91
+
92
+ return @valid
93
+ end
94
+
95
+ def run
96
+ settings = {
97
+ :host => @host, :port => @port,
98
+ :log_file => @log_file, :pid_file => @pid_file,
99
+ :docroot => @docroot,
100
+ :daemon => @daemon,
101
+ :ap4r_config_file => @ap4r_config_file,
102
+ }
103
+
104
+ config = ::Ap4r::Mongrel::Ap4rConfigurator.new(settings) do
105
+ if defaults[:daemon]
106
+ if File.exist? defaults[:pid_file]
107
+ log "PID file #{defaults[:pid_file]} exists!!! Exiting with error."
108
+ exit 1
109
+ end
110
+ daemonize({:log_file => @log_file, :cwd => File.expand_path(".") })
111
+ end
112
+
113
+ listener do
114
+ log "Starting AP4R Handler with #{defaults[:ap4r_config_file]}"
115
+ uri "/", :handler => ::Ap4r::Mongrel::Ap4rHandler.new(defaults)
116
+ end
117
+ setup_signals(settings)
118
+ end
119
+
120
+ config.run
121
+ config.log "Mongrel available at #{settings[:host]}:#{settings[:port]}"
122
+
123
+ if config.defaults[:daemon]
124
+ config.write_pid_file
125
+ else
126
+ config.log "Use CTRL-C to stop."
127
+ end
128
+
129
+ config.log "Mongrel start up process completed."
130
+ config.join
131
+
132
+ if config.needs_restart
133
+ if RUBY_PLATFORM !~ /mswin/
134
+ cmd = "ruby #{__FILE__} start #{original_args.join(' ')}"
135
+ config.log "Restarting with arguments: #{cmd}"
136
+ config.stop
137
+ config.remove_pid_file
138
+
139
+ if config.defaults[:daemon]
140
+ system cmd
141
+ else
142
+ STDERR.puts "Can't restart unless in daemon mode."
143
+ exit 1
144
+ end
145
+ else
146
+ config.log "Win32 does not support restarts. Exiting."
147
+ end
148
+ end
149
+ end
150
+
151
+ end
152
+
153
+ class Stop < GemPlugin::Plugin "/commands"
154
+ include ::Ap4r::Mongrel::Command::Base
155
+
156
+ def configure
157
+ options [
158
+ ['-c', '--chdir PATH', "Change to dir", :@cwd, Dir.pwd],
159
+ ['-P', '--pid FILE', "PID file", :@pid_file, "log/mongrel_ap4r.pid"],
160
+ ['-f', '--force', "Force the shutdown (kill -9).", :@force, false],
161
+ ['-w', '--wait SECONDS', "Wait SECONDS before forcing shutdown", :@wait, "0"],
162
+ ]
163
+ end
164
+
165
+ def validate
166
+ @cwd = File.expand_path(@cwd)
167
+ valid_dir? @cwd, "Path of chdir not valid: #@cwd "
168
+ Dir.chdir(@cwd)
169
+
170
+ valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
171
+
172
+ return @valid
173
+ end
174
+
175
+ def run
176
+ if @force
177
+ @wait.to_i.times do |waiting|
178
+ exit(0) if not File.exist? @pid_file
179
+ sleep 1
180
+ end
181
+ send_signal("KILL", @pid_file) if File.exist? @pid_file
182
+ else
183
+ send_signal("TERM", @pid_file)
184
+ end
185
+ end
186
+ end
187
+
188
+ class Restart < GemPlugin::Plugin "/commands"
189
+ include ::Ap4r::Mongrel::Command::Base
190
+
191
+ def configure
192
+ options [
193
+ ['-c', '--chdir PATH', "Change to dir", :@cwd, Dir.pwd],
194
+ ['-P', '--pid FILE', "PID file", :@pid_file, "log/mongrel_ap4r.pid"],
195
+ ]
196
+ end
197
+
198
+ def validate
199
+ @cwd = File.expand_path(@cwd)
200
+ valid_dir? @cwd, "Path of chdir not valid: #@cwd "
201
+ Dir.chdir(@cwd)
202
+
203
+ valid_dir? File.dirname(@pid_file), "Path to pid file not valid: #@pid_file"
204
+
205
+ return @valid
206
+ end
207
+
208
+ def run
209
+ send_signal("USR2", @pid_file)
210
+ end
211
+ end
212
+
213
+ # TODO: add Reload(reliable-msg) command class 2007/04/16 by shino
214
+
215
+ end
216
+
217
+
218
+ def main(args)
219
+ cmd_name = args.shift
220
+
221
+ begin
222
+ # TODO not all commands are implemented 2007/04/16 by shinohara
223
+ if %w(help version start stop restart reload).include? cmd_name
224
+ cmd_name = ::Ap4r::Mongrel::Command::PREFIX + cmd_name
225
+ end
226
+
227
+ command = GemPlugin::Manager.instance.create("/commands/#{cmd_name}", :argv => args)
228
+ rescue OptionParser::InvalidOption
229
+ STDERR.puts "#$! for command '#{cmd_name}'"
230
+ STDERR.puts "Try #{cmd_name} -h to get help."
231
+ return false
232
+ rescue
233
+ STDERR.puts "ERROR RUNNING '#{cmd_name}': #$!"
234
+ STDERR.puts "Use help command to get help"
235
+ return false
236
+ end
237
+
238
+ if not command.done_validating
239
+ if not command.validate
240
+ STDERR.puts "#{cmd_name} reported an error. Use mongrel_rails #{cmd_name} -h to get help."
241
+ return false
242
+ else
243
+ command.run
244
+ end
245
+ end
246
+
247
+ return true
248
+ end
249
+
250
+ # TODO: This script is not yet proper form of GemPlugin 2007/04/16 by shinohara
251
+ #GemPlugin::Manager.instance.load "ap4r" => GemPlugin::INCLUDE, "rails" => GemPlugin::EXCLUDE
252
+
253
+ unless main(ARGV)
254
+ exit(1)
255
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: ap4r
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.3.0
7
- date: 2007-04-06 00:00:00 +09:00
6
+ version: 0.3.1
7
+ date: 2007-04-24 00:00:00 +09:00
8
8
  summary: Asynchronous Processing for Ruby.
9
9
  require_paths:
10
10
  - lib
@@ -36,10 +36,12 @@ files:
36
36
  - doc
37
37
  - lib
38
38
  - MIT-LICENSE
39
+ - pkg
39
40
  - rails_plugin
40
41
  - Rakefile
41
42
  - README
42
43
  - script
44
+ - temp
43
45
  - bin/ap4r_setup
44
46
  - config/ap4r_settings.rb
45
47
  - config/log4r.yaml
@@ -53,10 +55,12 @@ files:
53
55
  - script/irm
54
56
  - script/loop.cmd
55
57
  - script/loop.rb
58
+ - script/mongrel_ap4r.rb
56
59
  - script/start
57
60
  - script/stop
58
61
  - lib/ap4r
59
62
  - lib/ap4r/message_store_ext.rb
63
+ - lib/ap4r/mongrel.rb
60
64
  - lib/ap4r/multi_queue.rb
61
65
  - lib/ap4r/queue_manager_ext.rb
62
66
  - lib/ap4r/queue_manager_ext_debug.rb
@@ -74,6 +78,7 @@ files:
74
78
  - lib/ap4r/util/loc.rb
75
79
  - lib/ap4r/util/queue_client.rb
76
80
  - lib/ap4r/version.rb
81
+ - lib/ap4r/xxx_create_table_for_saf.rb
77
82
  - lib/ap4r.rb
78
83
  test_files: []
79
84