ap4r 0.3.0 → 0.3.1

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