#-- # Copyright (c) 2006 Yugui # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; version 2.1 # of the License # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #++ require 'rubygems' rescue nil require 'optparse' require File.dirname(__FILE__) + File::SEPARATOR + 'ajp_rails_dispatcher.rb' # # AJP server that dispatches AJP requests into Rails. # class RailsRunner < Net::AJP13::Server # +option+:: Hash which contains server-wide options. def initialize(options) super(options['BIND_IP'], options['AJP_PORT']) @environment_options = options end # Dispatches +request+ into Rails and returns Net::AJP13::Response object # which contains the result of Rails processing. # # +request+:: Net::AJP13::Request object def process_request(request) logger.debug("processing #{request.path}") response = AjpRailsDispatcher.dispatch(request, AjpRailsRequest::DEFAULT_SESSION_OPTIONS, @environment_options) logger.debug("processed #{request.path}: #{response.code} #{response.message}") response end end # Maps long option names to internal hash keys. OPTION_NAMES_TO_INTERNAL_NAMES = { 'port' => 'AJP_PORT', 'host' => 'BIND_IP', 'environment' => 'RAILS_ENV', 'location' => 'APP_LOCATION', 'directory' => 'APP_DIRECTORY', 'prefix' => 'DISPATCHER_PREFIX', 'suffix' => 'DISPATCHER_SUFFIX', 'jvm-route' => 'LOAD_BALANCE_ID', 'serverid' => 'LOAD_BALANCE_ID', 'daemon' => 'IS_DAEMON' } DEFAULT_OPTIONS = { OPTION_NAMES_TO_INTERNAL_NAMES['environment'] => ENV['RAILS_ENV'] || 'production', OPTION_NAMES_TO_INTERNAL_NAMES['port'] => Net::AJP13::Constants::DEFAULT_PORT, OPTION_NAMES_TO_INTERNAL_NAMES['host'] => '127.0.0.1', OPTION_NAMES_TO_INTERNAL_NAMES['location'] => '/', OPTION_NAMES_TO_INTERNAL_NAMES['directory'] => '.' } # Parses command line options. # # Merges external configuration file into DEFAULT_OPTIONS if a file specified, # and merges command line options, and returns merged options. # def parse_options opts = DEFAULT_OPTIONS.dup cmd_opts = {} parser = OptionParser.new parser.on('-p PORT', '--port=PORT', "Listens to the specified port.\n" + "Default: #{opts[OPTION_NAMES_TO_INTERNAL_NAMES['port']]}") { |v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['port']] = v } parser.on('-h IP', '--host=IP', "Binds rails to the specified ip.\n" + "Default: #{opts[OPTION_NAMES_TO_INTERNAL_NAMES['host']]}") { cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['host']] = v } parser.on('-e RAILS_ENV', '--environment=RAILS_ENV', "Specifies the environment to run this server under (test/development/production).\n" + "Default: ENV['RAILS_ENV'] || 'production'") { |v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['environment']] = v } parser.on('-l LOCATION', '--location=LOCATION', "The base of the application's virtual path.\n" + "Default: /") { |v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['location']] = v } parser.on('-d DIRECTORY', '--directory=DIRECTORY', "The base of the application's physical path\n" + "Default: #{opts[OPTION_NAMES_TO_INTERNAL_NAMES['directory']]}") { |v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['directory']] = v } parser.on('--prefix=PREFIX', "The prefix of the ajp-mounted URLs.") { |v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['prefix']] = v } parser.on('--suffix=SUFFIX', "The suffix of the ajp-mounted URLs.") { |v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['suffix']] = v } parser.on('--serverid=ID', "The unique ID to identify this rails process," + " which is for sticky session. The ID can contain only" + " [a-z][A-Z][0-9], and case insensitive.") { |id| raise ArgumentError, 'Server ID can contain only [a-z][A-Z][0-9].' unless /\A[[:alnum:]]+\Z/ =~ id cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['serverid']] = id } parser.on('--jvm-route=ID', "This is equal to --serverid") { |id| raise ArgumentError, 'Server ID can contain only [a-z][A-Z][0-9].' unless /\A[[:alnum:]]+\Z/ =~ id cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['jvm-route']] = id } parser.on('--daemon', "Makes Rails run as a daemon") {|v| cmd_opts[OPTION_NAMES_TO_INTERNAL_NAMES['daemon']] = v } parser.on('-c FILE', '--config=FILE', 'Reads options from the specified file. ' + 'The file must be YAML file.') {|value| cmd_opts['config'] = value } parser.parse(ARGV) if path = cmd_opts['config'] cmd_opts.delete('config') if FileTest::file? path then require 'yaml' YAML.load_file(path).each do |key, value| name = OPTION_NAMES_TO_INTERNAL_NAMES[key] opts[name || key] = value end else warn("Not a exsiting regular file: #{path}") exit(1) end end opts.merge(cmd_opts) end options = parse_options if is_daemon = options[OPTION_NAMES_TO_INTERNAL_NAMES['daemon']] options.delete(OPTION_NAMES_TO_INTERNAL_NAMES['daemon']) exit if Process.fork Process.setsid exit if Process.fork File.umask(0) end raise if defined?(RAILS_ENV) ::RAILS_ENV = options['RAILS_ENV'].dup Dir.chdir options[OPTION_NAMES_TO_INTERNAL_NAMES['directory']] require 'config/environment' if is_daemon Dir.chdir('/') STDIN.reopen('/dev/null') STDOUT.reopen('/dev/null', 'w') STDERR.reopen('/dev/null', 'w') end runner = RailsRunner.new(options) runner.start