From 985bb4dd71f6a77a5f3855b677a978c04e69f613 Mon Sep 17 00:00:00 2001 From: luislavena Date: Tue, 21 Feb 2006 23:31:05 +0000 Subject: Adding mongrel_rails_service (and svc) drafts. Still fail to stop due Rails CGI Handler (my guess). Also fixed the "graceful stop" in mongrel_simple_service because cause also errors (how gracefully it is?) git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@55 19e92222-5c0b-0410-8929-a290d50e31e9 --- examples/mongrel_rails_service.rb | 237 +++++++++++++++++++++++++++++++++++++ examples/mongrel_rails_svc.rb | 194 ++++++++++++++++++++++++++++++ examples/mongrel_simple_service.rb | 2 +- 3 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 examples/mongrel_rails_service.rb create mode 100644 examples/mongrel_rails_svc.rb diff --git a/examples/mongrel_rails_service.rb b/examples/mongrel_rails_service.rb new file mode 100644 index 0000000..a0c4454 --- /dev/null +++ b/examples/mongrel_rails_service.rb @@ -0,0 +1,237 @@ +############################################### +# mongrel_rails_service.rb +# +# Control script for Rails powered by Mongrel +# +# WARNING: stop command still doesn't work with rails! +############################################### +require 'rubygems' +require 'mongrel' +require 'mongrel/command' + +require 'win32/service' +include Win32 + +module GenericCommand + def configure + options [ + ['-n', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil], + ] + end + + def validate + valid? @svc_name != nil, "You must specify the service name to be uninstalled." + + # We should validate service existance here, right Zed? + begin + valid? Service.exists?(@svc_name), "There is no service with that name, cannot proceed." + rescue + end + + return @valid + end +end + +class InstallCommand < Mongrel::Command::Command + + # Default every option to nil so only the defined ones get passed to service + # (which will override ServiceCommand defaults). + def configure + options [ + ['-n', '--name SVC_NAME', "Required name for the service to be registered/installed.", :@svc_name, nil], + ['-d', '--display SVC_DISPLAY', "Adjust the display name of the service.", :@svc_display, nil], + ['-r', '--root PATH', "Set the root path where your rails app resides.", :@rails_root, Dir.pwd], + ['-e', '--environment ENV', "Rails environment to run as", :@environment, 'production'], + ['-b', '--binding ADDR', "Address to bind to", :@ip, nil], + ['-p', '--port PORT', "Which port to bind to", :@port, 3000], + ['-m', '--mime PATH', "A YAML file that lists additional MIME types", :@mime_map, nil], + ['-P', '--num-procs INT', "Number of processor threads to use", :@num_procs, nil], + ['-t', '--timeout SECONDS', "Timeout all requests after SECONDS time", :@timeout, nil], + ] + end + + # When we validate the options, we need to make sure the --root is actually RAILS_ROOT + # of the rails application we wanted to serve, because later "as service" no error + # show to trace this. + def validate + @rails_root = File.expand_path(@rails_root) + + # start with the premise of app really exist. + app_exist = true + paths = %w{app config db log public} + paths.each do |path| + if !File.directory?(@rails_root + '/' + path) + app_exist = false + break + end + end + + valid? @svc_name != nil, "You must specify a valid service name to install." + valid? app_exist == true, "The root of rails app isn't valid, please verify." + valid_exists? @mime_map, "MIME mapping file does not exist: #@mime_map" if @mime_map + + # We should validate service existance here, right Zed? + begin + valid? !Service.exists?(@svc_name), "The service already exist, please uninstall it first." + rescue + end + + # Expand to get full path for mime-types file + @mime_map = File.Expand_path(@mime_map) if @mime_map + + # default service display to service name + @svc_display = @svc_name if !@svc_display + + return @valid + end + + def build_params + # build the parameters that will be used when register/install the service + @params = "" + + # add "service" command + @params << "service " + + # rails_root, must be quoted to support long_names + @params << "-r \"#{@rails_root}\" " + + # environment + @params << "-e #{@environment} " if @environment + + # binding + @params << "-b #{@ip} " if @ip + + # port + @params << "-p #{@port.to_i} " if @port + + # mime + @params << "-m #{@mime_map} " if @mime_map + + # num_procs + @params << "-P #{@num_procs.to_i} " if @num_procs + + # timeout + @params << "-t #{@timeout.to_i} " if @timeout + end + + def install_service + # use rbconfig to get the path to bin ruby.exe + require 'rbconfig' + + # ruby.exe instead of rubyw.exe due a exception raised when stoping the service! + binary_path = "" + binary_path << '"' << Config::CONFIG['bindir'] << '/ruby.exe' << '" ' + + # add service_script + service_script = File.expand_path(File.dirname(__FILE__) + '/mongrel_rails_svc.rb') + binary_path << '"' << service_script << '" ' + + # now add the parameters to it. + binary_path << @params + + puts "Installing service with these options:" + puts "service name: " << @svc_name + puts "service display: " << @svc_display + + puts "RAILS_ROOT: " << @rails_root + puts "RAILS_ENV: " << @environment if @environment + puts "binding: " << @ip if @ip + puts "port: " << @port.to_s if @port + + puts "mime_map: " << @mime_map if @mime_map + puts "num_procs: " << @num_procs.to_s if @num_procs + puts "timeout: " << @timeout.to_s if @timeout + + puts "ruby.exe: " << Config::CONFIG['bindir'] << '/ruby.exe' + puts "service script: " << service_script + puts + + svc = Service.new + begin + svc.create_service{ |s| + s.service_name = @svc_name + s.display_name = @svc_display + s.binary_path_name = binary_path + s.dependencies = [] + } + puts "#{@svc_display} service installed." + rescue ServiceError => err + puts "There was a problem installing the service:" + puts err + end + svc.close + end + + def run + build_params + install_service + end +end + +class DeleteCommand < Mongrel::Command::Command + include GenericCommand + + def run + display_name = Service.getdisplayname(@svc_name) + + begin + Service.stop(@svc_name) + rescue + end + begin + Service.delete(@svc_name) + rescue + end + puts "#{display_name} service deleted." + end +end + +class StartCommand < Mongrel::Command::Command + include GenericCommand + + def run + display_name = Service.getdisplayname(@svc_name) + + begin + Service.start(@svc_name) + started = false + while started == false + s = Service.status(@svc_name) + started = true if s.current_state == "running" + break if started == true + puts "One moment, " + s.current_state + sleep 1 + end + puts "#{display_name} service started" + rescue ServiceError => err + puts "There was a problem starting the service:" + puts err + end + end +end + +class StopCommand < Mongrel::Command::Command + include GenericCommand + + def run + display_name = Service.getdisplayname(@svc_name) + + begin + Service.stop(@svc_name) + stopped = false + while stopped == false + s = Service.status(@svc_name) + stopped = true if s.current_state == "stopped" + break if stopped == true + puts "One moment, " + s.current_state + sleep 1 + end + puts "#{display_name} service stopped" + rescue ServiceError => err + puts "There was a problem stopping the service:" + puts err + end + end +end + +Mongrel::Command::Registry.instance.run ARGV diff --git a/examples/mongrel_rails_svc.rb b/examples/mongrel_rails_svc.rb new file mode 100644 index 0000000..1ce8781 --- /dev/null +++ b/examples/mongrel_rails_svc.rb @@ -0,0 +1,194 @@ +############################################### +# mongrel_rails_svc.rb +# +# This is where Win32::Daemon resides. +############################################### +require 'rubygems' +require 'mongrel' + +require 'optparse' + +require 'win32/service' + +DEBUG_LOG_FILE = File.expand_path(File.dirname(__FILE__) + '/debug.log') +#STDERR.reopen(DEBUG_LOG_FILE) + +# There are need for SimpleHandler +require 'yaml' +require 'zlib' + +class RailsHandler < Mongrel::HttpHandler + + def initialize(dir, mime_map = {}) + @files = Mongrel::DirHandler.new(dir,false) + @guard = Mutex.new + + # register the requested mime types + mime_map.each {|k,v| Mongrel::DirHandler::add_mime_type(k,v) } + end + + def process(request, response) + # not static, need to talk to rails + return if response.socket.closed? + + if @files.can_serve(request.params["PATH_INFO"]) + @files.process(request,response) + else + cgi = Mongrel::CGIWrapper.new(request, response) + + begin + @guard.synchronize do + # Rails is not thread safe so must be run entirely within synchronize + Dispatcher.dispatch(cgi, ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS, response.body) + end + + # This finalizes the output using the proper HttpResponse way + cgi.out {""} + rescue Object => rails_error + STDERR.puts "calling Dispatcher.dispatch #{rails_error}" + STDERR.puts rails_error.backtrace.join("\n") + end + end + end + +end + +class SimpleHandler < Mongrel::HttpHandler + def process(request, response) + response.start do |head,out| + head["Content-Type"] = "text/html" + results = "Your request:
#{request.params.to_yaml}
View the files." + if request.params["HTTP_ACCEPT_ENCODING"] == "gzip,deflate" + head["Content-Encoding"] = "deflate" + # send it back deflated + out << Zlib::Deflate.deflate(results) + else + # no gzip supported, send it back normal + out << results + end + end + end +end + +class RailsDaemon < Win32::Daemon + def initialize(ip, port, rails_root, docroot, environment, mime_map, num_procs, timeout) + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - daemon_initialize entered") } + + @ip = ip + @port = port + @rails_root = rails_root + @docroot = docroot + @environment = environment + @mime_map = mime_map + @num_procs = num_procs + @timeout = timeout + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - daemon_initialize left") } + end + + def load_mime_map + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - load_mime_map entered") } + + mime = {} + + # configure any requested mime map + if @mime_map + puts "Loading additional MIME types from #@mime_map" + mime.merge!(YAML.load_file(@mime_map)) + + # check all the mime types to make sure they are the right format + mime.each {|k,v| puts "WARNING: MIME type #{k} must start with '.'" if k.index(".") != 0 } + end + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - load_mime_map left") } + + return mime + end + + def configure_rails + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - configure_rails entered") } + + Dir.chdir(@rails_root) + + ENV['RAILS_ENV'] = @environment + require File.join(@rails_root, 'config/environment') + + # configure the rails handler + rails = RailsHandler.new(@docroot, load_mime_map) + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - configure_rails left") } + + return rails + end + + def service_init + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init entered") } + + @rails = configure_rails + #@rails = SimpleHandler.new + + # start up mongrel with the right configurations + @server = Mongrel::HttpServer.new(@ip, @port, @num_procs.to_i, @timeout.to_i) + @server.register("/", @rails) + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_init left") } + end + + def service_main + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main entered") } + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - server.run") } + @server.run + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - while RUNNING") } + while state == RUNNING + sleep 1 + end + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_main left") } + end + + def service_stop + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_stop entered") } + + #File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - server.stop") } + #@server.stop + + File.open(DEBUG_LOG_FILE,"a+") { |f| f.puts("#{Time.now} - service_stop left") } + end +end + + +if ARGV[0] == 'service' + ARGV.shift + + # default options + OPTIONS = { + :rails_root => Dir.pwd, + :environment => 'production', + :ip => '0.0.0.0', + :port => 3000, + :mime_map => nil, + :num_procs => 20, + :timeout => 120 + } + + ARGV.options do |opts| + opts.on('-r', '--root PATH', "Set the root path where your rails app resides.") { |OPTIONS[:rails_root]| } + opts.on('-e', '--environment ENV', "Rails environment to run as.") { |OPTIONS[:environment]| } + opts.on('-b', '--binding ADDR', "Address to bind to") { |OPTIONS[:ip]| } + opts.on('-p', '--port PORT', "Which port to bind to") { |OPTIONS[:port]| } + opts.on('-m', '--mime PATH', "A YAML file that lists additional MIME types") { |OPTIONS[:mime_map]| } + opts.on('-P', '--num-procs INT', "Number of processor threads to use") { |OPTIONS[:num_procs]| } + opts.on('-t', '--timeout SECONDS', "Timeout all requests after SECONDS time") { |OPTIONS[:timeout]| } + + opts.parse! + end + + OPTIONS[:docroot] = File.expand_path(OPTIONS[:rails_root] + '/public') + + rails_svc = RailsDaemon.new(OPTIONS[:ip], OPTIONS[:port], OPTIONS[:rails_root], OPTIONS[:docroot], OPTIONS[:environment], OPTIONS[:mime_map], OPTIONS[:num_procs].to_i, OPTIONS[:timeout].to_i) + rails_svc.mainloop + +end + diff --git a/examples/mongrel_simple_service.rb b/examples/mongrel_simple_service.rb index 8e82c7b..3f9bc2c 100644 --- a/examples/mongrel_simple_service.rb +++ b/examples/mongrel_simple_service.rb @@ -51,7 +51,7 @@ class MongrelDaemon < Win32::Daemon f.puts "stop signal received: " + Time.now.to_s f.puts "sending stop to mongrel threads: " + Time.now.to_s } - @http_server.stop + #@http_server.stop end def service_pause -- cgit v1.2.3-24-ge0c7